#include "StdAfx.h"
#include <CryThread.h>
#include "MusicSystem.h"
#include <CrySizer.h>
#include <IRenderer.h>
#include <IAudioDevice.h>
#include "Decoder/PatternDecoder.h"
#include "IReverbManager.h"
#include "../SoundSystem.h"
#include "../FileCacheManager.h"
#include <IJobManager_JobDelegator.h>

#ifdef INCLUDE_AUDIO_PRODUCTION_CODE
enum EMusicInfoDebugFilter
{
	eMIDF_ALL         = BIT(6), // a
	eMIDF_INSTANCES		= BIT(7), // b
	eMIDF_PATTERNS    = BIT(8), // c
};
#endif // INCLUDE_AUDIO_PRODUCTION_CODE

//44100
enum {MixBufferSize = 16384};
enum {MixBufferCount = 8};





DECLARE_JOB("MixStreamsKernel", TMixJob, CMusicSystem::MixStreamsKernel );
static JobManager::SJobState g_MixJobState; 

//////////////////////////////////////////////////////////////////////////
// Global critical section used by music system.
//////////////////////////////////////////////////////////////////////////
//CryCriticalSection m_CSMusic_vecPlayingPatterns;
//CryCriticalSection m_CSMusic_FMOD_specific;
//CryCriticalSection m_CSMusic_MixBuffers;
//CryCriticalSection m_CSMusic_Tracks;
//CryCriticalSection m_CSMusic_Data_mapPatterns;
//CryCriticalSection m_CSMusic_Data_ThemesAndMoods;
//CryCriticalSection m_CSMusic_Data_ThemesAndMoodsPointers;
//CryCriticalSection m_CSMusic_PatternInstances;
//CryCriticalSection m_CSMusic_PatternInstancesToBeRemoved;
//CryCriticalSection m_CSMusic_PatternInstancesRemovedFromStreamingThread;
CryCriticalSection m_CS; 

//////////////////////////////////////////////////////////////////////////
// SmartCriticalSection-class to make things easier
//////////////////////////////////////////////////////////////////////////

/*
 *	INFO: You should read the document CryMusicSystem.doc first to know about the terminology used.
 */

/* streaming-callback called by fmod.
*/

#if defined SOUNDSYSTEM_USE_FMODEX400
//#pragma message (">>> Music-callback definition:")
FMOD_RESULT F_CALLBACK _StreamingCallback(FMOD_SOUND *pStream, void *pBuffer, unsigned int nLength)
{
	void* pParam = NULL;
	FMOD::Sound* const pSound = reinterpret_cast<FMOD::Sound* const>(pStream);
	pSound->getUserData(&pParam);

	if (pParam != NULL)
	{
		CMusicSystem* const pMusicSystem = static_cast<CMusicSystem*>(pParam);
		pMusicSystem->StreamingCallback(pStream, pBuffer, nLength);
	}

	return FMOD_OK;
}

#else
//void _StreamingCallback()	;
//	if (!nParam)
//		return false;
//
//	CMusicSystem *pMusicSystem=(CMusicSystem*)nParam;
//	return pMusicSystem->StreamingCallback(pStream, pBuffer, nLength);
//}
#endif

// Static member initialization
TMoodsHierarchyVec CMusicSystem::m_aoMoodsHierarchy;

/* constructor
*/
CMusicSystem::CMusicSystem()
{
#ifdef MUSIC_FORCE_QUEUED_TRACKS
	m_bForceQueuedRequests = true;
#else
	m_bForceQueuedRequests = false;
#endif // MUSIC_FORCE_QUEUED_TRACKS

	m_pAudioDevice               = gEnv->pSoundSystem->GetIAudioDevice();
	m_fMasterVolume              = gEnv->pSoundSystem->GetInterfaceExtended()->GetMusicVolume();
	m_nSampleRate                = 44100;
	m_nBytesPerSample            = 4; // always use STEREO 16 BIT
	m_fLatency                   = 0.1f;//to let it peak less
	m_pStream                    = NULL;
	m_pChannel                   = NULL;
	m_bPlaying                   = false;
	m_UpdateTicks                = 0;
	m_nLayeredRhythmicPatterns   = 0;
	m_nLayeredIncidentalPatterns = 0;
	m_pMixBuffer                 = NULL;
	m_pPatternMixBuffer          = NULL;
	m_pCurrTheme                 = NULL;
	m_pNextTheme                 = NULL;
	m_pCurrMood                  = NULL;
	m_pNextMood                  = NULL;
	m_bForcePatternChange        = false;
	m_fDefaultMoodTime           = 0.0f;
	m_sDefaultMood               = "";
	m_pSink                      = NULL;
	m_nDelayThemeInSec           = -1;
	m_bDataLoaded                = false;
	m_bPause                     = false;
	m_bBridging                  = false;
	m_bCurrPatternIsBridge       = false;
	m_bNeedUpdate                = false;
	m_fCurrCrossfadeTime         = DEFAULT_CROSSFADE_TIME;
	m_musicEnable                = g_nMusicEnable;
	m_bWaitForQueuedPatterns     = false;

	m_mapPatterns.clear();
	m_mapThemes.clear();
	m_PatternInstances.reserve(8);
	m_tLastUpdate.SetMilliSeconds(0);

	SetTrack1(NULL, MUSICLAYER_NONE);
	SetTrack2(NULL, MUSICLAYER_NONE);
	SetNextTrack(NULL, MUSICLAYER_NONE);

	m_aoMoodsHierarchy.push_back(SMusicMoodHierarchy("Silence", "Incidental", ""));
	m_aoMoodsHierarchy.push_back(SMusicMoodHierarchy("Incidental", "Ambient", "Silence"));
	m_aoMoodsHierarchy.push_back(SMusicMoodHierarchy("Ambient", "Tension", "Incidental"));
	m_aoMoodsHierarchy.push_back(SMusicMoodHierarchy("Tension", "Action", "Ambient"));
	m_aoMoodsHierarchy.push_back(SMusicMoodHierarchy("Action", "Frantic", "Tension"));
	m_aoMoodsHierarchy.push_back(SMusicMoodHierarchy("Frantic", "", "Action"));

	Init();
}

/* destructor; make sure everything gets unloaded
*/
CMusicSystem::~CMusicSystem()
{
	Unload();
}

/* destroy system
*/
void CMusicSystem::Release()
{
	Shutdown();
	delete this;
}

/* set up notification-sink
*/
IMusicSystemSink* CMusicSystem::SetSink(IMusicSystemSink *pSink)
{
	IMusicSystemSink *pOldSink = m_pSink;
	m_pSink = pSink;
	return pOldSink;
}

/* initialize system and create fmod-streaming callback
*/
bool CMusicSystem::Init()
{
	CryAutoCriticalSection SmartCriticalSection(m_CS);

	EnableEventProcessing(true);
	GetISystem()->GetISystemEventDispatcher()->RegisterListener(this);

	m_pMixBuffer = g_MemoryPoolPrimary.Allocate<void*>(MixBufferSize, CRYSOUNDMEMORY_ALLIGNMENT,"FMOD MusicMixBuffer");
	m_pPatternMixBuffer = CryModuleMemalign(MixBufferSize*MixBufferCount+32,128);

#if defined SOUNDSYSTEM_USE_FMODEX400

	if (m_pAudioDevice)
	{
		FMOD::System* pFMODEx = (FMOD::System*)m_pAudioDevice->GetSoundLibrary();
		if (pFMODEx)
		{
			FMOD_RESULT             result;
			FMOD::Sound            *sound = 0;
			FMOD_MODE               mode = (FMOD_MODE)(FMOD_2D | FMOD_SOFTWARE | FMOD_DEFAULT | FMOD_OPENUSER | FMOD_LOOP_NORMAL | FMOD_CREATESTREAM | PS3_MEM_FLAG | FMOD_NONBLOCKING);

			int                     channels = 2;
			FMOD_CREATESOUNDEXINFO  createsoundexinfo;
			memset(&createsoundexinfo, 0, sizeof(FMOD_CREATESOUNDEXINFO));
			createsoundexinfo.cbsize            = sizeof(FMOD_CREATESOUNDEXINFO);								/* required. */
			createsoundexinfo.decodebuffersize  = MixBufferSize;//44100;												/* Chunk size of stream update in samples.  This will be the amount of data passed to the user callback. */
			createsoundexinfo.length            = 44100 * channels * sizeof(signed short) * 10;	/* Length of PCM data in bytes of whole song (for Sound::getLength) */
			createsoundexinfo.numchannels       = channels;																			/* Number of channels in the sound. */
			createsoundexinfo.defaultfrequency  = 44100;																				/* Default playback rate of sound. */
			createsoundexinfo.format            = FMOD_SOUND_FORMAT_PCM16;											/* Data format of sound. */
			createsoundexinfo.pcmreadcallback   = _StreamingCallback;														/* User callback for reading. */
			createsoundexinfo.userdata					= (void*)this;

			result = pFMODEx->createSound(0, mode, &createsoundexinfo, &sound);
			assert(result != FMOD_ERR_NOTREADY);

			m_pStream = (tAssetHandle)sound;
		}
	}
#endif // SOUNDSYSTEM_USE_FMODEX400
	
	return m_pStream != NULL;
}

/* counterpart of init(); destroy stream and cleanup allocated mem.
*/
void CMusicSystem::Shutdown()
{
	EnableEventProcessing(false);
	GetISystem()->GetISystemEventDispatcher()->RemoveListener(this);
	StopPlaying(); // must be outside of critical section because Stop internally calls a CS

	CryAutoCriticalSection SmartCriticalSection(m_CS);
	m_vecLog.clear();
	Silence();

	if (m_pStream)
	{
#ifdef SOUNDSYSTEM_USE_FMODEX400
		FMOD::Sound* const pSound     = static_cast<FMOD::Sound* const>(m_pStream);
		FMOD::Channel* const pChannel = static_cast<FMOD::Channel* const>(m_pChannel);
		FMOD_RESULT eResult = FMOD_OK;
		
		if (pChannel != NULL)
		{
			eResult = pChannel->stop();
			assert(eResult == FMOD_OK);
		}
		
		if (pSound != NULL)
		{
			eResult = pSound->release();
			assert(eResult == FMOD_OK);
		}
#endif // SOUNDSYSTEM_USE_FMODEX400
	}
	
	m_pStream   = NULL;
	m_pChannel  = NULL;
	m_bPlaying  = false;
	bool bFreed = false;

	if (m_pMixBuffer)
	{
		bFreed = g_MemoryPoolPrimary.Free(m_pMixBuffer);
		m_pMixBuffer = NULL;
	}

	if (m_pPatternMixBuffer)
	{
		CryModuleMemalignFree(m_pPatternMixBuffer);
		m_pPatternMixBuffer = NULL;
	}
}

/* add (create) pattern to the music-system and create/open its decoder instance
*/
CMusicPattern* CMusicSystem::AddPattern(const char *pszName, const char *pszFilename)
{
	CryAutoCriticalSection SmartCriticalSection(m_CS);

	POOL_NEW_CREATE(CMusicPattern, pPattern)(this, pszName,pszFilename);

	if (pPattern)
	{
		// TODO: Verify if a Getsize could test for valid file and trigger file dependency
		// uint32 nSize = gEnv->pCryPak->FGetSize(pszName);

		//CryAutoCriticalSection SmartCriticalSection3(m_CSMusic_Data_mapPatterns);
		m_mapPatterns.insert(TPatternMapIt::value_type(pszName, pPattern));
	}
	
	return pPattern;
}

/* release all patterns
*/
bool CMusicSystem::FlushPatterns()
{
	CryAutoCriticalSection SmartCriticalSection(m_CS);

	if ( m_bPlaying )
	{
		Silence();
		StopPlaying();
	}

	ReleaseData();

	m_mapThemes.clear();
	
	m_pCurrTheme = NULL;
	m_pNextTheme = NULL;
	m_pCurrMood  = NULL;
	m_pNextMood  = NULL;

	// Make sure to clean the system of any playing or queued up patterns.
	ForceFinishAllPatterns();

	SetTrack1(NULL, MUSICLAYER_NONE);
	SetTrack2(NULL, MUSICLAYER_NONE);
	SetNextTrack(NULL, MUSICLAYER_NONE);

	// Pattern map
	TPatternMapItConst const ItPatternMapEnd(m_mapPatterns.end());

	for (TPatternMapItConst It(m_mapPatterns.begin()); It != ItPatternMapEnd; ++It)
	{
		CMusicPattern *pPattern = It->second;
		delete pPattern;
	}

	m_mapPatterns.clear();

	stl::free_container(m_sDefaultMood);

	m_nLayeredRhythmicPatterns   = 0;
	m_nLayeredIncidentalPatterns = 0;
	m_bForcePatternChange        = false;
	m_fDefaultMoodTime           = 0.0f;
	m_bDataLoaded                = false;
	m_bPause                     = false;
	m_bBridging                  = false;
	m_bCurrPatternIsBridge       = false;

	return true;
}

/* start streaming (so callback will be called)
*/
bool CMusicSystem::StartPlaying()
{
	if (!m_musicEnable)
		return false;

	CryAutoCriticalSection SmartCriticalSection(m_CS);
	
	if (!m_bDataLoaded)
		return false;
	
	if (m_bPause)
		return true;
	
	if (m_pChannel != NULL)
		return false;
	
	m_fMasterVolume = gEnv->pSoundSystem->GetInterfaceExtended()->GetMusicVolume();
	
	if (m_fMasterVolume <= 0)
		return true;

#ifdef SOUNDSYSTEM_USE_FMODEX400
	//CryAutoCriticalSection SmartCriticalSection1(m_CSMusic_FMOD_specific);

	FMOD::System* pFMODEx = (FMOD::System*)m_pAudioDevice->GetSoundLibrary();
	if (pFMODEx)
	{
		FMOD::Channel *pChannel = (FMOD::Channel*)m_pChannel;
		m_ExResult = pFMODEx->playSound(FMOD_CHANNEL_FREE, (FMOD::Sound*)m_pStream, true, &pChannel);
		
		if (m_ExResult != FMOD_OK || pChannel==NULL)
		{
			CryLogAlways("<Sound> Start playing music failed! \n");
			return false;
		}

		// boost Priority
		pChannel->setPriority(0);

		// set the music channel into the music category of the eventsystem
		FMOD::EventSystem* pEventSystem = (FMOD::EventSystem*)m_pAudioDevice->GetEventSystem();
		if (pEventSystem && g_nMusicCategory)
		{
			FMOD::EventCategory* pMasterCat = NULL;
			FMOD::EventCategory* pMusicCat = NULL;
			m_ExResult = pEventSystem->getCategory("master", &pMasterCat);

			if (m_ExResult == FMOD_OK)
			{
				m_ExResult = pMasterCat->getCategory("music", &pMusicCat);

				if (m_ExResult == FMOD_OK)
				{
					FMOD::ChannelGroup* pChannelGroup = NULL;
					m_ExResult = pMusicCat->getChannelGroup(&pChannelGroup);
					m_ExResult = pChannel->setChannelGroup(pChannelGroup);
				}
			}
		}

		FMOD_SPEAKERMODE SpeakerMode = FMOD_SPEAKERMODE_RAW;
		m_ExResult = pFMODEx->getSpeakerMode(&SpeakerMode);

		if (SpeakerMode > FMOD_SPEAKERMODE_STEREO)
		{
			m_ExResult = pChannel->setSpeakerMix(	g_fMusicSpeakerFrontVolume,
																						g_fMusicSpeakerFrontVolume,
																						g_fMusicSpeakerCenterVolume,
																						g_fMusicSpeakerLFEVolume,
																						g_fMusicSpeakerBackVolume,
																						g_fMusicSpeakerBackVolume,
																						g_fMusicSpeakerSideVolume,
																						g_fMusicSpeakerSideVolume);
		}

		FMOD_REVERB_CHANNELPROPERTIES TempReverb = {0};
		m_ExResult = pChannel->getReverbProperties(&TempReverb);
		TempReverb.Room = -10000;
		m_ExResult = pChannel->setReverbProperties(&TempReverb);
		m_ExResult = pChannel->setPaused(false);
		m_pChannel = pChannel;
		m_bPlaying = true;
	}
#endif

	if (m_pChannel == NULL)
	{
		FlushPatterns();
		return false;
	}

#ifdef SOUNDSYSTEM_USE_FMODEX400
	FMOD::Channel *pChannel = (FMOD::Channel*)m_pChannel;
	m_ExResult = pChannel->setVolume(m_fMasterVolume); 
#endif

	return true;
}

/* stop streaming; streaming-callback will not be called anymore
*/
bool CMusicSystem::StopPlaying()
{
	if (!m_musicEnable)
		return false;

	//CryAutoCriticalSection SmartCriticalSection(m_CSMusic_FMOD_specific);

	if (m_pChannel == NULL)
		return false;

#ifdef SOUNDSYSTEM_USE_FMODEX400
	FMOD::Channel *pChannel = (FMOD::Channel*)m_pChannel;
	m_ExResult = pChannel->setPaused(true);
	m_ExResult = pChannel->stop();
	//m_pStream=NULL;
#endif

	m_bPlaying = false;
	m_pChannel = NULL;
	m_bWaitForQueuedPatterns = false;
	
	return true;
}

/* set music-system data (eg. read from lua-file by gamecode)
	we convert all names to lowercase to make them not case-sensitive (faster lookup)
	we start streaming
*/
bool CMusicSystem::SetData( SMusicInfo::Data *pMusicData )
{
	
	StopPlaying(); // must be outside of critical section because Stop internally calls a CS
	
	CryAutoCriticalSection SmartCriticalSection(m_CS);
	
	FlushPatterns();

	if (!pMusicData)
		return true;

	int i;

	//////////////////////////////////////////////////////////////////////////
	// Set themes.
	{
		//CryAutoCriticalSection SmartCriticalSection(m_CSMusic_Data_ThemesAndMoods);
		m_mapThemes.clear();
		for (i = 0; i < pMusicData->nThemes; i++)
		{
			POOL_NEW_CREATE(SMusicTheme, pTheme);

			if (pTheme)
			{
				pTheme->fDefaultMoodTimeout = pMusicData->pThemes[i].fDefaultMoodTimeout;
				pTheme->sDefaultMood = pMusicData->pThemes[i].sDefaultMood;
				pTheme->sName = pMusicData->pThemes[i].sName;
				m_mapThemes[pTheme->sName] = pTheme;
			}
		}
	}

	//////////////////////////////////////////////////////////////////////////
	// Set Moods.
	for (i = 0; i < pMusicData->nMoods; i++)
	{
		//CryAutoCriticalSection SmartCriticalSection(m_CSMusic_Data_ThemesAndMoods);

		SMusicInfo::Mood &moodInfo =  pMusicData->pMoods[i];
		SMusicTheme *pTheme = stl::find_in_map( m_mapThemes,moodInfo.sTheme,NULL );
		if (!pTheme)
			continue;

		POOL_NEW_CREATE(SMusicMood, pMood);

		if (pMood)
		{
			pMood->sName = moodInfo.sName;
			pMood->bPlaySingle = moodInfo.bPlaySingle;
			pMood->fFadeOutTime = moodInfo.fFadeOutTime;
			pMood->nPriority = moodInfo.nPriority;

			pMood->vecPatternSets.resize( moodInfo.nPatternSetsCount );
			for (int j = 0; j < moodInfo.nPatternSetsCount; ++j)
			{
				POOL_NEW_CREATE(SMusicPatternSet, pPatternSet);

				if (pPatternSet)
				{
					pMood->vecPatternSets[j] = pPatternSet;

					pPatternSet->fMaxTimeout													= moodInfo.pPatternSets[j].fMaxTimeout;
					pPatternSet->fMinTimeout													= moodInfo.pPatternSets[j].fMinTimeout;

					pPatternSet->fIncidentalLayerProbabilityInPercent	= moodInfo.pPatternSets[j].fIncidentalLayerProbabilityInPercent;
					pPatternSet->fRhythmicLayerProbabilityInPercent		= moodInfo.pPatternSets[j].fRhythmicLayerProbabilityInPercent;

					pPatternSet->fTotalMainPatternProbability					= moodInfo.pPatternSets[j].fTotalMainPatternProbability;
					pPatternSet->fTotalIncidentalPatternProbability		= moodInfo.pPatternSets[j].fTotalIncidentalPatternProbability;
					pPatternSet->fTotalStingerPatternProbability			= moodInfo.pPatternSets[j].fTotalStingerPatternProbability;
					pPatternSet->fTotalRhythmicPatternProbability			= moodInfo.pPatternSets[j].fTotalRhythmicPatternProbability; // This wasn't here before...a mistake?

					pPatternSet->nMaxSimultaneousIncidentalPatterns		= moodInfo.pPatternSets[j].nMaxSimultaneousIncidentalPatterns;
					pPatternSet->nMaxSimultaneousRhythmicPatterns			= moodInfo.pPatternSets[j].nMaxSimultaneousRhythmicPatterns;

					pPatternSet->bPlayIncidentalPatternSynced					= moodInfo.pPatternSets[j].bPlayIncidentalPatternSynced;
					pPatternSet->bPlayRhythmicPatternSynced						= moodInfo.pPatternSets[j].bPlayRhythmicPatternSynced;
				}
			}
			pTheme->mapMoods[pMood->sName] = pMood;
		}
	}
	
	//////////////////////////////////////////////////////////////////////////
	// Set Patterns.
	for (i = 0; i < pMusicData->nPatterns; i++)
	{
		SMusicInfo::Pattern &patternInfo = pMusicData->pPatterns[i];
		SMusicTheme *pTheme = stl::find_in_map( m_mapThemes, patternInfo.sTheme, NULL );
		if (!pTheme)
			continue;

		SMusicMood *pMood = stl::find_in_map( pTheme->mapMoods, patternInfo.sMood, NULL );
		if (!pMood)
			continue;

		POOL_NEW_CREATE(SPatternDef, pPatternDef);

		if (pPatternDef)
		{
			pPatternDef->fProbabilityPoints = patternInfo.fProbabilityPoints;
			pPatternDef->fLayeringVolume = patternInfo.fLayeringVolume;
			pPatternDef->sFilename = patternInfo.sFilename;
			pPatternDef->sName = patternInfo.sName;
			pPatternDef->nPreFadeIn = patternInfo.nPreFadeIn;

			pPatternDef->vecFadePoints.reserve(pPatternDef->vecFadePoints.size() + patternInfo.nFadePointsCount);
			for (int j = 0; j < patternInfo.nFadePointsCount; j++)
				pPatternDef->vecFadePoints.push_back(patternInfo.pFadePoints[j]);

			assert( patternInfo.nPatternSetIndex >= 0 && patternInfo.nPatternSetIndex < (int)pMood->vecPatternSets.size() );
			SMusicPatternSet *pPatternSet = pMood->vecPatternSets[patternInfo.nPatternSetIndex];
			switch (patternInfo.nPatternType)
			{
			case MUSICLAYER_MAIN:
				pPatternSet->vecMainPatterns.push_back( pPatternDef );
				break;
			case MUSICLAYER_INCIDENTAL:
				pPatternSet->vecIncidentalPatterns.push_back( pPatternDef );
				break;
			case MUSICLAYER_RHYTHMIC:
				pPatternSet->vecRhythmicPatterns.push_back( pPatternDef );
				break;
			case MUSICLAYER_START:
				pPatternSet->vecStartPatterns.push_back( pPatternDef );
				break;
			case MUSICLAYER_END:
				pPatternSet->vecEndPatterns.push_back( pPatternDef );
				break;
			case MUSICLAYER_STINGER:
				pPatternSet->vecStingerPatterns.push_back( pPatternDef );
				pPatternDef->fProbabilityPoints = 1.0f;
				break;
			}

			CMusicPattern *pPattern = AddPattern(pPatternDef->sName.c_str(), pPatternDef->sFilename.c_str());
			if (!pPattern)
			{
				gEnv->pSystem->Warning( VALIDATOR_MODULE_SYSTEM,VALIDATOR_WARNING,VALIDATOR_FLAG_SOUND,pPatternDef->sFilename.c_str(),
					"<Music> File %s failed to load",pPatternDef->sFilename.c_str() );
				continue;
			}
			pPattern->PrepareForMoreFadePoints(pPatternDef->vecFadePoints.size());
			for (TIntVecIt IntIt=pPatternDef->vecFadePoints.begin();IntIt!=pPatternDef->vecFadePoints.end();++IntIt)
			{
				pPattern->AddFadePoint(*IntIt);
			}
			pPattern->SetPreFadeIn(pPatternDef->nPreFadeIn);
			pPattern->SetLayeringVolume(pPatternDef->fLayeringVolume);
		}
	}

	m_bDataLoaded = true;
	
	// start playing the stream...
	//if (m_pChannel == NULL)
	//	StartPlaying();
	
	return true;
}

void CMusicSystem::ReleaseData()
{
	// release themes/moods
	for (TThemeMapIt ThemeIt=m_mapThemes.begin();ThemeIt!=m_mapThemes.end();++ThemeIt)
	{
		SMusicTheme *pTheme=ThemeIt->second;
		for (TMoodMapIt MoodIt=pTheme->mapMoods.begin();MoodIt!=pTheme->mapMoods.end();++MoodIt)
		{
			SMusicMood *pMood=MoodIt->second;
			for (TPatternSetVecIt PatternSetIt=pMood->vecPatternSets.begin();PatternSetIt!=pMood->vecPatternSets.end();++PatternSetIt)
			{
				SMusicPatternSet *pPatternSet=(*PatternSetIt);

				TPatternDefVec::iterator PatternIt;
				for (PatternIt = pPatternSet->vecMainPatterns.begin();PatternIt!=pPatternSet->vecMainPatterns.end();++PatternIt)
				{
					SPatternDef *pPattern = (*PatternIt);
					POOL_FREE(pPattern);
				}
				for (PatternIt = pPatternSet->vecRhythmicPatterns.begin();PatternIt!=pPatternSet->vecRhythmicPatterns.end();++PatternIt)
				{
					SPatternDef *pPattern = (*PatternIt);
					POOL_FREE(pPattern);
				}
				for (PatternIt = pPatternSet->vecIncidentalPatterns.begin();PatternIt!=pPatternSet->vecIncidentalPatterns.end();++PatternIt)
				{
					SPatternDef *pPattern = (*PatternIt);
					POOL_FREE(pPattern);
				}
				for (PatternIt = pPatternSet->vecStartPatterns.begin();PatternIt!=pPatternSet->vecStartPatterns.end();++PatternIt)
				{
					SPatternDef *pPattern = (*PatternIt);
					POOL_FREE(pPattern);
				}
				for (PatternIt = pPatternSet->vecEndPatterns.begin();PatternIt!=pPatternSet->vecEndPatterns.end();++PatternIt)
				{
					SPatternDef *pPattern = (*PatternIt);
					POOL_FREE(pPattern);
				}
				for (PatternIt = pPatternSet->vecStingerPatterns.begin();PatternIt!=pPatternSet->vecStingerPatterns.end();++PatternIt)
				{
					SPatternDef *pPattern = (*PatternIt);
					POOL_FREE(pPattern);
				}
				POOL_FREE(pPatternSet);
			}
			POOL_FREE(pMood);
		}
		POOL_FREE(pTheme);
	}
	m_mapThemes.clear();
}


/* stop streaming and unload all data from the system
*/
void CMusicSystem::Unload()
{
	StopPlaying();  // must be outside of critical section because Stop internally calls a CS

	// stop the stream...
	CryAutoCriticalSection SmartCriticalSection(m_CS);
	Silence();
	FlushPatterns();
	FlushLog();
	stl::free_container(m_vecLog);
	stl::free_container(m_aoQueuedRequests);
	stl::free_container(m_apEventListeners);
	stl::free_container(m_aeQueuedMusicSystemEvents);

	ReleaseXMLNode(m_oMusicStateNode);
}

/* pause/resume streaming
*/
void CMusicSystem::Pause(bool bPause)
{
	if (m_bPause != bPause || m_bPlaying == bPause)
	{
		m_bPause = bPause;
		if (bPause)
			StopPlaying(); // must be outside of critical section because Stop internally calls a CS
		else
			StartPlaying();
	}
}

/* get pointer to current mood descriptor
*/
SMusicMood* CMusicSystem::GetMood(SMusicTheme *pTheme, const char *pszMood) const
{
	CryAutoCriticalSection SmartCriticalSection(m_CS);

	if (!pTheme)
		return NULL;
	
	TMoodMapIt It=pTheme->mapMoods.find(CONST_TEMP_STRING(pszMood));
	
	if (It==pTheme->mapMoods.end())
		return NULL;
	
	return It->second;
}

/* set the current theme by pointer to descriptor
*/
bool CMusicSystem::SetTheme(SMusicTheme *pNewTheme, bool bForceChange)
{
	//CryAutoCriticalSection SmartCriticalSection(m_CSMusic_Data_ThemesAndMoodsPointers);

	if (pNewTheme == m_pCurrTheme)
		return false;	// do nothing since we're already in the requested theme
	
	if (!((m_pCurrTheme == m_pNextTheme) || (pNewTheme)))	// only switch to the new theme if there was no switch in this frame yet OR the new theme is NOT null
		return true;
	
	if (pNewTheme)
		LogMsg(1, "<Music> Setting theme to %s with%s forcing change.", pNewTheme->sName.c_str(), bForceChange?"":"out");
	else
	{
		LogMsg(1, "<Music> Setting theme to NULL");
		SetMood( (SMusicMood*) NULL, 1);

	}
	
	m_pNextTheme = pNewTheme;

	//m_bForcePatternChange = bForceChange;
	
	m_ThemeTime = gEnv->pTimer->GetFrameStartTime();

	return true;
}

/* set the current theme by name
*/
bool CMusicSystem::SetTheme(const char *pszTheme, bool bForceChange/* =true */, bool bKeepMood/* =true */, int nDelayInSec/* =-1 */, char const* const sStartMood /* = NULL */)
{
	bool bResult = false;
	
	if (!m_bForceQueuedRequests && m_CS.TryLock())
	{
		// no theme
		if (*pszTheme==0)
		{
			bool const bRes = SetTheme((SMusicTheme*)NULL, false);
			m_CS.Unlock();
			return bRes;
		}

		//CryAutoCriticalSection SmartCriticalSection1(m_CSMusic_Data_ThemesAndMoods);

		TThemeMapIt It = m_mapThemes.find(CONST_TEMP_STRING(pszTheme));
		if (It==m_mapThemes.end())
		{
			LogMsg(1, "<Music> Theme is now known, aborting setting new theme: <%s>", pszTheme);
			m_CS.Unlock();
			return false;	// theme not found
		}

		// make sure music plays
		Pause(false);

		SMusicTheme *pNewTheme = It->second;

		if (nDelayInSec >= 0)
		{
			m_ThemeSetTime = gEnv->pTimer->GetFrameStartTime();
			m_nDelayThemeInSec = nDelayInSec;
		}
		else
			m_nDelayThemeInSec = -1;

		pNewTheme->bKeepMood  = bKeepMood;
		pNewTheme->sStartMood = sStartMood;
		bResult               = SetTheme(pNewTheme, bForceChange);

		if (bKeepMood && (m_nDelayThemeInSec == -1))
		{
			if (bResult)
			{
				//CryAutoCriticalSection SmartCriticalSection2(m_CSMusic_vecPlayingPatterns);
				//CryAutoCriticalSection SmartCriticalSection3(m_CSMusic_Tracks);

				for (TPatternPlayInfoVecIt VecIt=m_vecPlayingPatterns.begin();VecIt!=m_vecPlayingPatterns.end();++VecIt)
				{
					//SMusicPatternPlayInfo *pPlayInfo = GetPlayingPatternInfo(MUSICLAYER_MAIN, EBlend_Once);
					SMusicPatternPlayInfo *pPlayInfo = &(*VecIt);
					if (pPlayInfo->pPatternInstance == m_pTrack1)
					{
						{
							//CryAutoCriticalSection SmartCriticalSection4(m_CSMusic_Data_ThemesAndMoodsPointers);
							pPlayInfo->fFadeTime	= m_pCurrMood ? m_pCurrMood->fFadeOutTime : 1.0f;
						}

						pPlayInfo->eBlendType = EBlend_FadeOut;
						pPlayInfo->ePlayingType = EPlaying_Loop;
						m_bForcePatternChange = false;
						LogMsg(3, "<Music> Start to fade out %s", pPlayInfo->pPatternInstance->GetPattern()->GetName());
					}
				}
			}

			bResult = SetMood( GetMood(), 1, bForceChange|bKeepMood);
			//m_bForcePatternChange = true;
		}

		if (!bResult)
		{
			SetMood( pNewTheme->sDefaultMood, 1, bForceChange);
			m_fDefaultMoodTime = -1.0f;
			//m_bForcePatternChange = bForceChange;
		}

		m_CS.Unlock();
	}
	else
	{
		SMusicSystemQueuedRequest oQueuedRequest;
		oQueuedRequest.eMusicSystemQueudRequestType = eMSQRT_SetTheme;
		oQueuedRequest.sName												= pszTheme;
		oQueuedRequest.bForceChange									= bForceChange;
		oQueuedRequest.bKeepMood										= bKeepMood;
		oQueuedRequest.nDelayInSec									= nDelayInSec;

		m_aoQueuedRequests.push_back(oQueuedRequest);

		bResult = true;
	}

	return bResult;
}

/* Brings the current theme to a graceful end
*/
bool CMusicSystem::EndTheme(EThemeFadeType ThemeFade/* =EThemeFade_FadeOut */, int nForceEndLimitInSec/* =10 */, bool bEndEverything/* =false */)
{
	if (!m_bForceQueuedRequests && m_CS.TryLock())
	{
		if (m_pCurrMood)
			m_pCurrMood->bStopped = true;

		if (ThemeFade==EThemeFade_StopAtOnce)
		{
			for (TPatternPlayInfoVecIt It=m_vecPlayingPatterns.begin();It!=m_vecPlayingPatterns.end();++It)
			{
				SMusicPatternPlayInfo &PlayInfo = (*It);
				if (bEndEverything || !(PlayInfo.nLayer & MUSICLAYER_PATTERN_BY_NAME))
				{
					PlayInfo.ePlayingType = EPlaying_Stop;
					PlayInfo.nLayer = MUSICLAYER_END;
				}
			}
			LogMsg(2, "<Music> EndTheme at once");

			if (m_pCurrMood)
				m_pCurrMood->bMainSpawned = false;

			QueueEvent(MUSICSYSTEM_EVENT_ON_THEME_END);

			m_CS.Unlock();

			return true;
		}

		if (ThemeFade==EThemeFade_PlayEnd || ThemeFade== EThemeFade_PlayEndAtFadePoint)
		{
			int nSamplesToEnd = 0;

			if (ThemeFade==EThemeFade_PlayEnd)
			{
				if (m_pTrack1 && m_Track1Type==MUSICLAYER_MAIN)
					nSamplesToEnd = m_pTrack1->GetSamplesToEnd();

				if (m_pTrack2 && m_Track2Type==MUSICLAYER_MAIN)
					nSamplesToEnd = max(nSamplesToEnd, m_pTrack2->GetSamplesToEnd());
			}
			else
			{
				if (m_pTrack1 && m_Track1Type==MUSICLAYER_MAIN)
					nSamplesToEnd = m_pTrack1->GetSamplesToNextFadePoint();

				if (m_pTrack2 && m_Track2Type==MUSICLAYER_MAIN)
					nSamplesToEnd = max(nSamplesToEnd, m_pTrack2->GetSamplesToNextFadePoint());

			}

			int nSecToEnd = nSamplesToEnd/44100;

			if (nSecToEnd < nForceEndLimitInSec)
			{
				SetNextTrack(ChoosePattern(m_pCurrMood, MUSICLAYER_END), MUSICLAYER_END);

				if (m_pNextTrack)
				{
					LogMsg(2, "<Music> EndTheme End found will play in %d seconds", nSecToEnd);

					if (ThemeFade== EThemeFade_PlayEndAtFadePoint)
						m_pNextTrack->m_bPlayAtOnce = true;

					for (TPatternPlayInfoVecIt It=m_vecPlayingPatterns.begin();It!=m_vecPlayingPatterns.end();++It)
					{
						SMusicPatternPlayInfo &PlayInfo = (*It);
						if ((bEndEverything && (PlayInfo.nLayer & MUSICLAYER_PATTERN_BY_NAME)) || (ThemeFade == EThemeFade_PlayEndAtFadePoint))
						{
							PlayInfo.eBlendType = (ThemeFade==EThemeFade_PlayEndAtFadePoint)?EBlend_EndOnFadePoint:EBlend_FadeOutOnMainEnd;
							PlayInfo.nLayer = MUSICLAYER_END;
						}
					}

					QueueEvent(MUSICSYSTEM_EVENT_ON_THEME_END);
					
					m_CS.Unlock();
					
					return true;
				}
			}
		}

		// if none of the above or no end was selected
		LogMsg(2, "<Music> EndTheme FadeOut or no end found within %d seconds", nForceEndLimitInSec);

		TPatternPlayInfoVec::const_iterator const IterEnd(m_vecPlayingPatterns.end());

		for (TPatternPlayInfoVec::iterator Iter(m_vecPlayingPatterns.begin()); Iter != IterEnd; ++Iter)
		{
			SMusicPatternPlayInfo& rPlayInfo = (*Iter);
			rPlayInfo.eBlendType = EBlend_FadeOut;
			rPlayInfo.nLayer     = MUSICLAYER_END;
		}

		QueueEvent(MUSICSYSTEM_EVENT_ON_THEME_END);

		m_CS.Unlock();
		
		return true;
	}
	else
	{
		SMusicSystemQueuedRequest oQueuedRequest;
		oQueuedRequest.eMusicSystemQueudRequestType = eMSQRT_EndTheme;
		oQueuedRequest.eThemeFadeType								= ThemeFade;
		oQueuedRequest.nForceEndLimitInSec					= nForceEndLimitInSec;
		oQueuedRequest.bEndEverything								= bEndEverything;

		m_aoQueuedRequests.push_back(oQueuedRequest);
	}

	return true;
}

/* 
set the current mood by pointer to descriptor
*/
//////////////////////////////////////////////////////////////////////////
bool CMusicSystem::SetMood(SMusicMood *pNewMood, const bool bPlayFromStart, const bool bForceChange)
{
	if (!pNewMood)
	{
		// clear out all queued patterns of this mood
		for(TPatternPlayInfoVecIt it=m_vecQueuedPatterns.begin(); it != m_vecQueuedPatterns.end(); )
		{
			TPatternInstancePtr pPatternInstance = it->pPatternInstance;
			if(m_pCurrMood && pPatternInstance && pPatternInstance->GetMood() && pPatternInstance->GetMood() == m_pCurrMood)
			{
				m_PatternInstancesToBeRemoved.push_back(pPatternInstance);
				it = m_vecQueuedPatterns.erase(it);
				continue;
			}
			++it;
		}

		m_pCurrMood = pNewMood;
		m_pNextMood = NULL;
		SetNextTrack(NULL, MUSICLAYER_NONE);

		LogMsg(1, "<Music> Setting mood to <none>");

		// The mood has changed even though disabled.
		QueueEvent(MUSICSYSTEM_EVENT_ON_MOOD_SWITCH);

		return true;
	}

	pNewMood->bStopped = false;

	if (pNewMood==m_pCurrMood && (m_pTrack1 || m_pTrack2) && (m_pCurrMood && m_pCurrMood->bMainSpawned))
	{
		return true;	// do nothing since we're already in the requested mood
	}

	LogMsg(1, "<Music> Setting mood to %s with %s", pNewMood->sName.c_str(), bPlayFromStart?"queueing":"crossfading");

	// adjust fading time according to new pattern
	m_fCurrCrossfadeTime = pNewMood->fFadeOutTime;

	pNewMood->bPlayFromStart = bPlayFromStart;

	if (!UpdateCurrentPatternSet(pNewMood, 0, true))
	{
		LogMsg(1, "[Warning] <Music> Unable to find pattern-set in mood %s!", pNewMood->sName.c_str() );
		return false;
	}

	m_bForcePatternChange = ((m_pCurrMood!=NULL) && (m_pCurrMood!=pNewMood)) || bForceChange;	// try to get into the new mood asap if we're changing the mood
	m_pCurrMood = pNewMood;

	// no pattern chosen yet, so music should begin with start if available
	bool bStartPlayed = false;

	if (bPlayFromStart)
		bStartPlayed = PlayStart(bForceChange?EBlend_None:EBlend_PlayOnFadePoint);

	// Before creating a new pattern instance with the given mood, check if it already exists in the PlayingPatterns, if so, 
	// just fade that in instead of creating a new one.
	CryFixedStringT<256> sTempPatternName;
	TPatternInstancePtr  pTempPatternInstance = ChoosePattern(pNewMood, MUSICLAYER_MAIN);

	bool bExistInPlayingPattern = false;

	TPatternPlayInfoVec::const_iterator IterPatternEnd(m_vecPlayingPatterns.end());

	for (TPatternPlayInfoVec::iterator IterPattern(m_vecPlayingPatterns.begin()); IterPattern != IterPatternEnd; ++IterPattern)
	{
		SMusicPatternPlayInfo& PlayInfo = (*IterPattern);
		sTempPatternName = PlayInfo.pPatternInstance->GetPattern()->GetName();

		if ((sTempPatternName.compareNoCase(pTempPatternInstance->GetPattern()->GetName()) == 0))
		{
			PlayInfo.eBlendType = EBlend_FadeIn;
			PlayInfo.nLayer     = MUSICLAYER_MAIN;
			// If the new requested pattern doesn't lie on track1 track2 or next track then set it up.

			if (m_pTrack1 != pTempPatternInstance && m_pTrack2 != pTempPatternInstance && m_pNextTrack != pTempPatternInstance)
			{
				SetNextTrack(PlayInfo.pPatternInstance, MUSICLAYER_MAIN);
			}

			m_PatternInstancesToBeRemoved.push_back(pTempPatternInstance);
			bExistInPlayingPattern = true;
		}
		else 
		{
			PlayInfo.eBlendType = EBlend_FadeOut; //If the playing pattern is different from the requested pattern then fadeout the present one. 
		}
	}
	
	// If the pattern with the given mood already exist in the playing patterns then fade it in and return true.
	if (bExistInPlayingPattern)
	{
		return true;
	}

	if (!bStartPlayed)
	{
		SMusicPatternPlayInfo	PlayInfo;
		PlayInfo.pPatternInstance = pTempPatternInstance;
		PlayInfo.nLayer						= MUSICLAYER_MAIN;
		PlayInfo.ePlayingType			= m_pCurrMood->bPlaySingle ? EPlaying_Once : EPlaying_Loop;
		PlayInfo.fPhase						= 1.0f;
		PlayInfo.fFadeTime				= m_pCurrMood->fFadeOutTime;
		PlayInfo.bForceChange			= bForceChange;
		PlayInfo.pPatternInstance->m_bPlayAtOnce    = (!bStartPlayed && bForceChange);
		PlayInfo.pPatternInstance->m_bPlayFromStart = bPlayFromStart;

		if (bPlayFromStart)
		{
			PlayInfo.eBlendType = bForceChange?EBlend_None:EBlend_PlayOnFadePoint;
		}
		else
		{
			PlayInfo.fPhase     = 0.0f;
			PlayInfo.eBlendType = bForceChange?EBlend_FadeIn:EBlend_FadeInOnFadePoint;
		}

		if (bForceChange)
		{
			int nSamplePosToGoTo = 0;

			if (m_pTrack1 && !bPlayFromStart)
			{
				SMusicPatternFileInfo FileInfoNext;
				PlayInfo.pPatternInstance->GetPattern()->GetFileInfo(FileInfoNext);

				if (FileInfoNext.nSamples > 0)
					nSamplePosToGoTo = m_pTrack1->GetPos() % FileInfoNext.nSamples;
			}

			PlayInfo.pPatternInstance->SeekPos(nSamplePosToGoTo);
		}

		PlayInfo.bFadeOutMainTracks = true;

		if (PushPatternToMixList(PlayInfo))
		{
			// If there is something playing on the main tracks fade it out
			FadeOutMainTracks(bForceChange);

			SetTrack2(pTempPatternInstance, MUSICLAYER_MAIN);
			SetNextTrack(NULL, MUSICLAYER_NONE);
			m_bForcePatternChange = false; // TODO Check, prevent double playback
		}
	}

	m_MoodTime = gEnv->pTimer->GetFrameStartTime();

	// The mood got switched to a new mood.
	QueueEvent(MUSICSYSTEM_EVENT_ON_MOOD_SWITCH);

	return true;
}

/* set the current mood by name
*/
bool CMusicSystem::SetMood(const char *pszMood, const bool bPlayFromStart/* =true */, const bool bForceChange/* =false */)
{
	if (!m_bForceQueuedRequests && m_CS.TryLock())
	{
		LogMsg(1, "<Music> Setting new mood: <%s>", pszMood);

		SMusicTheme *pTheme = m_pCurrTheme;

		if (m_pNextTheme)
			pTheme = m_pNextTheme;

		if (!pTheme)
		{
			LogMsg(1, "<Music> No theme is set currently, aborting setting new mood: <%s>", pszMood);
			m_CS.Unlock();
			return false;	// no theme set
		}

		if (*pszMood==0)
		{
			SetMood((SMusicMood*)NULL);
			m_CS.Unlock();
			return true;
		}

		TMoodMapIt It = pTheme->mapMoods.find(CONST_TEMP_STRING(pszMood));

		if (It==pTheme->mapMoods.end())
		{
			LogMsg(1, "<Music> Current theme <%s> does not have this mood, aborting setting new mood: %s", pTheme->sName.c_str(), pszMood);
			m_CS.Unlock();
			return false;	// mood not found
		}

		SMusicMood *pNewMood=It->second;
		pNewMood->pSpawnedFromTheme = pTheme;

		if (m_pNextTheme)
			m_pNextMood = pNewMood;

		bool const bResult = SetMood(pNewMood, bPlayFromStart, bForceChange);;
		m_CS.Unlock();
		return bResult;
	}
	else
	{
		SMusicSystemQueuedRequest oQueuedRequest;
		oQueuedRequest.eMusicSystemQueudRequestType = eMSQRT_SetMood;
		oQueuedRequest.sName												= pszMood;
		oQueuedRequest.bPlayFromStart								= bPlayFromStart;
		oQueuedRequest.bForceChange									= bForceChange;

		m_aoQueuedRequests.push_back(oQueuedRequest);

		return true;
	}
}

/* set the current default mood by name
*/
bool CMusicSystem::SetDefaultMood(const char *pszMood)
{
	CryAutoCriticalSection SmartCriticalSection(m_CS);
	//CryAutoCriticalSection SmartCriticalSection(m_CSMusic_Data_ThemesAndMoodsPointers);

	m_sDefaultMood = pszMood;
	SMusicMood *pDefaultMood = GetDefaultMood(m_pCurrTheme);

	// force reselection of mood if we're currently playing the default mood
	if (m_fDefaultMoodTime < 0.0f)
	{
		// we're currently playing the default mood so lets set the new default-mood
		EnterDefaultMood();
		//m_fDefaultMoodTime = 0.0f;
	}
	else
	{
		// we're currently playing some non-default mood, lets check if the new default mood-priority is higher than the currently playing one
		if (pDefaultMood && m_pCurrMood)
		{
			if (pDefaultMood->nPriority > m_pCurrMood->nPriority)
				m_fDefaultMoodTime = 0.0f;	// default-mood priority is higher so lets choose it
		}
	}
	if (m_sDefaultMood.empty())
		LogMsg(1, "<Music> Setting theme-specific default mood");
	else
		LogMsg(1, "<Music> Setting default mood to %s", pszMood);

	return true;
}

/* get a vector of strings containing all available themes
*/
IStringItVec* CMusicSystem::GetThemes() const
{
	CryAutoCriticalSection SmartCriticalSection(m_CS);
	//CryAutoCriticalSection SmartCriticalSection1(m_CSMusic_Data_ThemesAndMoods);

	TStringVec Vec;
	for (TThemeMapItConst It=m_mapThemes.begin();It!=m_mapThemes.end();++It)
	{
		Vec.push_back(It->first.c_str());
	}
	
	POOL_NEW_CREATE(CStringItVec, pVec)(Vec);
	return pVec;
}

/* get a vector of strings containing all available moods in the theme pszTheme
*/
IStringItVec* CMusicSystem::GetMoods(const char *pszTheme) const
{
	CryAutoCriticalSection SmartCriticalSection(m_CS);
	//CryAutoCriticalSection SmartCriticalSection1(m_CSMusic_Data_ThemesAndMoods);

	TStringVec Vec;
	TThemeMapItConst It=m_mapThemes.find(CONST_TEMP_STRING(pszTheme));
	
	if (It==m_mapThemes.end())
		return NULL;
	
	SMusicTheme *pTheme=It->second;
	for (TMoodMapIt  Iter=pTheme->mapMoods.begin();Iter!=pTheme->mapMoods.end();++Iter)
	{
		Vec.push_back(Iter->first.c_str());
	}

	POOL_NEW_CREATE(CStringItVec, pVec)(Vec);
	return pVec;
}

//////////////////////////////////////////////////////////////////////////
IStringItVec* const CMusicSystem::GetPatterns() const
{
	CryAutoCriticalSection SmartCriticalSection(m_CS);

	TStringVec Vec;
	TPatternMapItConst IterEnd(m_mapPatterns.end());

	for (TPatternMapItConst Iter(m_mapPatterns.begin()); Iter != IterEnd; ++Iter)
	{
		Vec.push_back(Iter->first.c_str());
	}

	POOL_NEW_CREATE(CStringItVec, pVec)(Vec);

	return pVec;
}

/* add a mood-event
*/
bool CMusicSystem::AddMusicMoodEvent(const char *pszMood, float fTimeout)
{
	CryAutoCriticalSection SmartCriticalSection(m_CS);
	SMoodEventInfo EventInfo;
	EventInfo.sMood = pszMood;
	EventInfo.fTime = fTimeout;
	m_setFrameMoodEvents.insert(TMoodEventSetIt::value_type(EventInfo));
	return true;
}

/* remove all patterns from the play-lists
*/
void CMusicSystem::Silence()
{
	CryAutoCriticalSection SmartCriticalSection(m_CS);
	
	SetTrack1(NULL, MUSICLAYER_NONE);
	SetTrack2(NULL, MUSICLAYER_NONE);
	SetNextTrack(NULL, MUSICLAYER_NONE);
	
	m_pCurrTheme	  = NULL;
	m_pNextTheme	  = NULL;
	m_pCurrMood     = NULL;
	m_pNextMood     = NULL;

	// Make sure to clean the system of any playing or queued up patterns.
	ForceFinishAllPatterns();

	stl::free_container(m_sDefaultMood);

	m_nLayeredRhythmicPatterns   = 0;
	m_nLayeredIncidentalPatterns = 0;
	m_bForcePatternChange        = false;
	m_fDefaultMoodTime           = 0.0f;
	m_bPause                     = false;
	m_bBridging                  = false;
	m_bCurrPatternIsBridge       = false;
}

//returns and resets music system update time
float CMusicSystem::GetUpdateMilliseconds()
{
	float fTimeMS = SUpdateTimer::ConvertToMS(m_UpdateTicks);
	m_UpdateTicks = 0;
	return fTimeMS;
}

bool CMusicSystem::AllPatternsAreReady() const
{
	SMusicPatternFileInfo oMusicPatternFileInfo;
	for (TPatternPlayInfoVec::const_iterator it = m_vecQueuedPatterns.begin(); it != m_vecQueuedPatterns.end(); ++it)
	{
		const SMusicPatternPlayInfo& PlayInfo = *it;
		CMusicPatternInstance* const pPatternInstance = PlayInfo.pPatternInstance;

		assert(pPatternInstance);

		if (pPatternInstance)
		{
			CMusicPattern* const pMusicPattern = pPatternInstance->GetPattern();

			assert(pMusicPattern);

			if (pMusicPattern)
			{
				IMusicPatternDecoder* const pMusicPatternDecoder = pMusicPattern->GetDecoder();

				assert(pMusicPatternDecoder);

				if (pMusicPatternDecoder)
				{
					pMusicPatternDecoder->GetFileInfo(oMusicPatternFileInfo);

					EAudioFileOpenState state1 = oMusicPatternFileInfo.eAudioFileOpenState;
					EAudioFileOpenState state2 = pPatternInstance->GetDecoderInstance()->GetOpenState();

					// Ignore errors
					if (state1 != eAFOS_ERROR && state2 != eAFOS_ERROR)
					{
						if (state1 == eAFOS_LOADING || state2 == eAFOS_LOADING)
						{
							return false;
						}
					}
				}
			}
		}
	}
	return true;
}

/* main update-loop; check comments in function for details
*/
void CMusicSystem::Update()
{
	UPDATE_TIMER
	CTimeValue tFrameTime = gEnv->pTimer->GetAsyncTime(); // have to use aSync timer so that time updates correctly whilst loading on the rendering thread

	if( m_bNeedUpdate == false )
	{	
		// Update the MusicSystem every 250ms		
		CTimeValue tTimeDif = tFrameTime - m_tLastUpdate;
		float fMS = tTimeDif.GetMilliSeconds();

		if ( abs(fMS) < 250)
			return;
	}

	if( !m_CS.TryLock() )
	{
		// if we didn't acquire the lock, try again during the next frame
		m_bNeedUpdate = true;
		return;
	}

	m_bNeedUpdate = false;		

	bool bTrackUpdates = true;
	if(m_bWaitForQueuedPatterns)
	{
		if(!AllPatternsAreReady())
		{
			bTrackUpdates = false;
		}
		else
		{
			m_bWaitForQueuedPatterns = false;
		}
	}

	// Process queued requests if there are any.
	// Temporarily reset the bool so tracks don't get queued again.
#ifdef MUSIC_FORCE_QUEUED_TRACKS
	m_bForceQueuedRequests = false;
#endif // MUSIC_FORCE_QUEUED_TRACKS

	TQueuedRequestsVecItConst const IterEnd = m_aoQueuedRequests.end();

	for (TQueuedRequestsVecItConst Iter = m_aoQueuedRequests.begin(); Iter != IterEnd; ++Iter)
	{
		SMusicSystemQueuedRequest oQueuedRequest = (*Iter);

		switch (oQueuedRequest.eMusicSystemQueudRequestType)
		{
		case eMSQRT_SetMood:
			{
				assert(oQueuedRequest.sName.c_str());
				SetMood(oQueuedRequest.sName.c_str(), oQueuedRequest.bPlayFromStart, oQueuedRequest.bForceChange);
				break;
			}
		case eMSQRT_SetTheme:
			{
				assert(oQueuedRequest.sName.c_str());
				SetTheme(oQueuedRequest.sName.c_str(), oQueuedRequest.bForceChange, oQueuedRequest.bKeepMood, oQueuedRequest.nDelayInSec);
				break;
			}
		case eMSQRT_EndTheme:
			{
				EndTheme(oQueuedRequest.eThemeFadeType, oQueuedRequest.nForceEndLimitInSec, oQueuedRequest.bEndEverything);
				break;
			}
		case eMSQRT_PlayPattern:
			{
				assert(oQueuedRequest.sName.c_str());
				PlayPattern(oQueuedRequest.sName.c_str(), oQueuedRequest.bStopPrevious, oQueuedRequest.bPlaySynched, oQueuedRequest.bPlayOnTop);
				break;
			}
		case eMSQRT_PlayStinger:
			{
				PlayStinger();
				break;
			}
		case eMSQRT_PlayStart:
			{
				PlayStart(oQueuedRequest.eBlendingType);
				break;
			}
		}
	}

	m_aoQueuedRequests.clear();

#ifdef MUSIC_FORCE_QUEUED_TRACKS
	m_bForceQueuedRequests = true;
#endif // MUSIC_FORCE_QUEUED_TRACKS

	//CryLog(" MusicUpdate Start" );
	//float f0 = gEnv->pTimer->GetAsyncCurTime();

	m_tLastUpdate = tFrameTime;

	if (!m_pCurrTheme || !m_musicEnable)    
	{
		m_MoodTime = gEnv->pTimer->GetFrameStartTime();
		m_ThemeTime = gEnv->pTimer->GetFrameStartTime();
	}

	// Theme and Mood time for debug reasons
	//int nThemeInSecs = (int)GetThemeTime().GetSeconds();
	//int nMoodInSecs = (int)GetMoodTime().GetSeconds();

	FlushLog();

	if (!m_bDataLoaded)
	{
		m_CS.Unlock();
		return;	// no data loaded
	}

	FRAME_PROFILER( "MusicSystem::Update",GetISystem(),PROFILE_MUSIC );

	if (g_nMusicEnable != m_musicEnable)
	{
		if (!g_nMusicEnable)
		{
			StopPlaying(); // must be outside of critical section because Stop internally calls a CS
			Silence();
			m_musicEnable = g_nMusicEnable;
		}
		else
			m_musicEnable = g_nMusicEnable;
	}
	
	float fDeltaTime = 0.0f;

	if (m_musicEnable)
	{
		// get deltatime
		fDeltaTime = gEnv->pTimer->GetFrameTime();

		// check if the volume has changed...
		if (m_fMasterVolume != gEnv->pSoundSystem->GetInterfaceExtended()->GetMusicVolume())
		{
			m_fMasterVolume = gEnv->pSoundSystem->GetInterfaceExtended()->GetMusicVolume();
			if (m_fMasterVolume > 0.0f)
			{
				// verify
				StartPlaying();	// start playing in case we're stopped (volume=0)

#ifdef SOUNDSYSTEM_USE_FMODEX400
				{
					//CryAutoCriticalSection SmartCriticalSection(m_CS);
					if (m_pChannel != NULL)
					{
						FMOD::Channel *pChannel = (FMOD::Channel*)m_pChannel;
						m_ExResult = pChannel->setVolume(m_fMasterVolume);
					}
				}
#endif

			}
			else
			{
				// must be outside of critical section because Stop internally calls a CS
				StopPlaying();	// volume=0, lets stop playing the stream to reduce overhead
			}
		}

		// Process the pattern queue and check if it's safe to push the content to the pattern mix list
		if(!m_vecQueuedPatterns.empty())
		{
			bool bStartPlaying = false;

			SMusicPatternFileInfo oMusicPatternFileInfo;
			for (TPatternPlayInfoVec::iterator it = m_vecQueuedPatterns.begin(); it != m_vecQueuedPatterns.end(); )
			{
				SMusicPatternPlayInfo& PlayInfo = *it;
				CMusicPatternInstance* const pPatternInstance = PlayInfo.pPatternInstance;

				assert(pPatternInstance);

				if (pPatternInstance)
				{
					CMusicPattern* const pMusicPattern = pPatternInstance->GetPattern();

					assert(pMusicPattern);

					if (pMusicPattern)
					{
						IMusicPatternDecoder* const pMusicPatternDecoder = pMusicPattern->GetDecoder();

						assert(pMusicPatternDecoder);

						if (pMusicPatternDecoder)
						{
							
							// Start Playing from the position it is supposed to play i.e. if 0 then play from the starting else from the given position.
							if (PlayInfo.nStartFromPos != 0)
							{
								pPatternInstance->SeekPos(PlayInfo.nStartFromPos);
							}

							pMusicPatternDecoder->GetFileInfo(oMusicPatternFileInfo);

							if (oMusicPatternFileInfo.eAudioFileOpenState == eAFOS_READY &&
								pPatternInstance->GetDecoderInstance()->GetOpenState() == eAFOS_READY)
							{
								// If there is something playing on the main tracks fade it out
								if (PlayInfo.bFadeOutMainTracks)
								{
									FadeOutMainTracks(PlayInfo.bForceChange);
								}

								if (PlayInfo.bForceTrack1)
								{
									SetTrack1(pPatternInstance, PlayInfo.nLayer);
								}
								else if (PlayInfo.bForceTrack2)
								{
									SetTrack2(pPatternInstance, PlayInfo.nLayer);
								}
								else
								{
									SetNextTrack(pPatternInstance, PlayInfo.nLayer);
								}

								m_bForcePatternChange	= PlayInfo.bForceChange;

								PlayInfo.bForceTrack1 = false;
								PlayInfo.bForceTrack2 = false;

								m_vecPlayingPatterns.push_back(PlayInfo);

								if (PlayInfo.pPatternInstance->m_bPlayAtOnce)
								{
									bStartPlaying |= true;
								}

								if (PlayInfo.nLayer == MUSICLAYER_PATTERN_BY_NAME)
								{
									CMusicPatternInstance const* const pMusicPatternInstance = PlayInfo.pPatternInstance;

									if (pMusicPatternInstance)
									{
										CMusicPattern const* const pMusicPatternInst = pMusicPatternInstance->GetPattern();

										if (pMusicPatternInst)
										{
											m_oMusicSystemInfo.sMusicPatternNameTrack1 = pMusicPatternInst->GetName();
										}
									}

									// We need a name for this event to be successful
									assert(!m_oMusicSystemInfo.sMusicPatternNameTrack1.empty());
									QueueEvent(MUSICSYSTEM_EVENT_ON_PATTERN_START);
								}

								it = m_vecQueuedPatterns.erase(it);
								break;
							}

							// In the event of a load failure remove it from the queue
							// We shouldn't get here since it means we just lost a pattern to not enough memory
							if (pPatternInstance->GetDecoderInstance()->GetOpenState() == eAFOS_ERROR
								|| oMusicPatternFileInfo.eAudioFileOpenState == eAFOS_ERROR)
							{
								it = m_vecQueuedPatterns.erase(it);
								break;
							}
						}
					}
				}
				++it;
			}

			if (bStartPlaying)
			{
				StartPlaying();
			}
		}
	}

	{
		//CryAutoCriticalSection SmartCriticalSection2(m_CSMusic_PatternInstances);

		if (!m_PatternInstances.empty())
		{

			// MAIN THREAD processing
			// Go through all Pattern Instances to allow them to release themselves from here
			{
				//CryAutoCriticalSection SmartCriticalSection(m_CSMusic_PatternInstancesToBeRemoved);
				m_PatternInstancesToBeRemoved.reserve(m_PatternInstances.size());
			}

			{
				//CryAutoCriticalSection SmartCriticalSection(m_CS);

				TPatternInstancesVecIt ItEnd = m_PatternInstances.end();
				for (TPatternInstancesVecIt It=m_PatternInstances.begin(); It!=ItEnd; )
				{
					CMusicPatternInstance *pInstance = (*It);
					IMusicPatternDecoderInstance* decoderInstance = pInstance->GetDecoderInstance();
					if (decoderInstance)
					{
						decoderInstance->Init();

						CMusicPattern* const pMusicPattern = pInstance->GetPattern();
						IMusicPatternDecoder* const pMusicPatternDecoder = pMusicPattern->GetDecoder();

						SMusicPatternFileInfo oMusicPatternFileInfo;
						pMusicPatternDecoder->GetFileInfo(oMusicPatternFileInfo);

						// In case there was a load error remove it as well (error occurred most likely because of mem-alloc fail)
						if (decoderInstance->GetOpenState() == eAFOS_ERROR
							|| oMusicPatternFileInfo.eAudioFileOpenState == eAFOS_ERROR)
						{
							// Release all smart pointers to this pattern and destroy it
							if(m_pTrack1 == pInstance)
							{
								SetTrack1(NULL, MUSICLAYER_NONE);
							}

							if(m_pTrack2 == pInstance)
							{
								SetTrack2(NULL, MUSICLAYER_NONE);
							}

							if(m_pNextTrack == pInstance)
							{
								SetNextTrack(NULL, MUSICLAYER_NONE);
							}

							size_t nSize = m_vecPlayingPatterns.size();

							for(size_t i = nSize; i--;)
								if(m_vecPlayingPatterns[i].pPatternInstance == pInstance)
									m_vecPlayingPatterns.erase(m_vecPlayingPatterns.begin() + i);

							nSize = m_vecQueuedPatterns.size();

							for(size_t i = nSize; i--; )
								if(m_vecQueuedPatterns[i].pPatternInstance == pInstance)
									m_vecQueuedPatterns.erase(m_vecQueuedPatterns.begin() + i);

							nSize = m_vecQueuedLoadPatterns.size();

							for(size_t i = nSize; i--; )
								if(m_vecQueuedLoadPatterns[i].pPatternInstance == pInstance)
									m_vecQueuedLoadPatterns.erase(m_vecQueuedLoadPatterns.begin() + i);

							assert(pInstance->GetRefCount() == 1);
						}
					}

					if (pInstance->GetRefCount() == 1)
					{
						//CryAutoCriticalSection SmartCriticalSection3(m_CSMusic_PatternInstancesToBeRemoved);
						m_PatternInstancesToBeRemoved.push_back(pInstance);

						if (It < ItEnd-1)
						{
							// Swap Last and current
							(*It) = *(ItEnd-1);
						}
						m_PatternInstances.pop_back(); // remove Last
						ItEnd = m_PatternInstances.end(); // renew
					}
					else
						++It;
				}
			} // end of critical section#

			{
				//CryAutoCriticalSection SmartCriticalSection4(m_CSMusic_PatternInstancesRemovedFromStreamingThread);
				m_PatternInstancesRemovedFromStreamingThread.resize(0);
			}

			// Process the pattern queue and check if it's safe to push the content to the pattern mix list
			TPatternInstancesVec::const_iterator const IterToBeRemovedEnd(m_PatternInstancesToBeRemoved.end());

			for (TPatternInstancesVec::iterator Iter(m_PatternInstancesToBeRemoved.begin()); Iter != IterToBeRemovedEnd; ++Iter)
			{
				if ((*Iter)->IsReleasable())
				{
					m_PatternInstancesToBeRemoved.erase(Iter);
					break; // Continue next Update!
				}
			}
		}
	}


	if (!m_musicEnable) // Put this back here so we still remove pattern instance accumulated with music disabled
	{
		m_CS.Unlock();
		return;
	}

	if (bTrackUpdates)
	{
		if (m_nDelayThemeInSec > 0 && (m_ThemeSetTime.GetSeconds() + m_nDelayThemeInSec <= gEnv->pTimer->GetFrameStartTime().GetSeconds()))
		{
			m_ThemeSetTime.SetSeconds(0.0f);
			m_nDelayThemeInSec = -1;
			m_bForcePatternChange = true;
		}

		// Check for theme change, initiate a change only if we're not supposed to delay
		if (m_pCurrTheme != m_pNextTheme && m_nDelayThemeInSec <= 0)
		{
			// A new theme just started
			QueueEvent(MUSICSYSTEM_EVENT_ON_THEME_START);

			SMusicTheme *pNext = NULL;

			pNext = m_pNextTheme;

			m_bForcePatternChange = ((m_pCurrTheme!=NULL) && (m_pCurrTheme!=pNext) && m_nDelayThemeInSec==-1) || m_bForcePatternChange;	// try to get into the new theme asap if we're changing the theme
			if (m_pCurrTheme && pNext && m_bForcePatternChange)	// if we are already playing a theme we need to use a bridge
			{
				CMusicPatternInstance *pPattern = ChooseBridge(m_pCurrTheme, pNext);

				if (!pPattern)
				{
					LogMsg(1, "[Warning] <Music> Unable to find bridging pattern or no bridge defined from %s to %s !", m_pCurrTheme->sName.c_str(), m_pNextTheme->sName.c_str());
					// No bridge pattern, 
				}
				else
				{
					SetNextTrack(pPattern, MUSICLAYER_NONE);
				}
			}

			m_pCurrTheme = pNext;

			if (m_pCurrTheme)
			{
				if (m_bForcePatternChange)
				{
					if (m_pNextMood )	// reenter next mood to get it from the correct theme
					{
						if (m_pNextMood->pSpawnedFromTheme != m_pCurrMood->pSpawnedFromTheme)
							SetMood( m_pNextMood );
					}
					else if (pNext->bKeepMood)
					{
						SetMood(GetMood(), true, true);
					}
					else if (!pNext->sStartMood.empty())
					{
						SetMood(pNext->sStartMood.c_str(), true, true);
					}
					else
					{
						m_pCurrMood = NULL;
					}
				}
			}
			else
			{
				//CryAutoCriticalSection SmartCriticalSection8(m_CSMusic_vecPlayingPatterns);
				for (TPatternPlayInfoVecIt It=m_vecPlayingPatterns.begin();It!=m_vecPlayingPatterns.end();++It)
				{
					SMusicPatternPlayInfo &PlayInfo = (*It);
					if (!(PlayInfo.nLayer & MUSICLAYER_PATTERN_BY_NAME))
					{
						PlayInfo.ePlayingType = EPlaying_Stop;
					}
				}
			}

			if (!m_pCurrMood)
				EnterDefaultMood();	// if we are in no mood right now, use default mood
		}
		else
		{
			//m_bForcePatternChange = false;
		}

		m_pNextMood = NULL;

		// event-handling
		if (m_setFrameMoodEvents.empty())
		{
			// no events occured this frame so lets delete the events-list
			if (!m_setMoodEvents.empty())
				m_setMoodEvents.clear();
		}
		// lets add all new events
		for (TMoodEventSetIt It=m_setFrameMoodEvents.begin();It!=m_setFrameMoodEvents.end();++It)
		{
			TMoodEventSetIt EventIt=m_setMoodEvents.find(TMoodEventSetIt::value_type(*It));
			if (EventIt==m_setMoodEvents.end())	// event not in list yet
				m_setMoodEvents.insert(TMoodEventSetIt::value_type(*It));
		}
		// lets not choose any mood which has a lower priority than the currently playing one...
		int nNewMoodPriority = -1;

		{
			//CryAutoCriticalSection SmartCriticalSection9(m_CSMusic_Data_ThemesAndMoodsPointers);
			if (m_pCurrMood)
				nNewMoodPriority = m_pCurrMood->nPriority;
		}

		SMusicMood *pNewMood = NULL;
		for (TMoodEventSetIt It=m_setMoodEvents.begin();It!=m_setMoodEvents.end();)
		{
			// lets remove all events which didnt occur this frame...
			TMoodEventSetIt FrameIt=m_setFrameMoodEvents.find(TMoodEventSetIt::value_type(*It));
			if (FrameIt==m_setFrameMoodEvents.end())	// event not in list of last frame
			{
				// the event didn't occur, so we remove it here
				TMoodEventSetIt itnext = It;
				++itnext;
				m_setMoodEvents.erase(It);
				It = itnext;
			}else
			{
				// the event occured, so lets check if we reached the timeout
				SMoodEventInfo &EventInfo = const_cast<SMoodEventInfo&>(*It);
				EventInfo.fTime -= fDeltaTime;
				if (EventInfo.fTime <= 0.0f)
				{
					//CryAutoCriticalSection SmartCriticalSection10(m_CSMusic_Data_ThemesAndMoodsPointers);
					// time to switch to this mood, so we store its priority so we can choose the one with the highest priority later
					SMusicMood *pMood = GetMood(m_pCurrTheme, EventInfo.sMood.c_str());
					if (pMood)
					{
						// does it have a higher priority than the one before ?
						if (pMood->nPriority > nNewMoodPriority)
						{
							// ...yes, store name and new priority
							nNewMoodPriority = pMood->nPriority;
							pNewMood = pMood;
						}
					}else
					{
						if (m_pCurrTheme)
							LogMsg(1, "[Warning] <Music> Invalid music mood '%s' (Theme: %s)", EventInfo.sMood.c_str(), m_pCurrTheme->sName.c_str());
						else
							LogMsg(1, "[Warning] <Music> Invalid music mood '%s' (Theme: NULL)", EventInfo.sMood.c_str());
					}
				}
				++It;
			}
		}
		
		m_setFrameMoodEvents.clear();

		{
			//CryAutoCriticalSection SmartCriticalSection11(m_CSMusic_Data_ThemesAndMoodsPointers);
			// now we need to check if the current default mood has a higher priority than the current mood
			SMusicMood *pDefMood = GetDefaultMood(m_pCurrTheme);
			// select default mood, if...
			if (pDefMood																// ...there is a default mood
				&& (pDefMood->nPriority > nNewMoodPriority)	// ...it has a higher priority than the new mood
				&& (m_fDefaultMoodTime > 0.0f))						// ...we are not in the default mood already
			{
				// update default mood timing
				m_fDefaultMoodTime -= fDeltaTime;
				// is it time to enter the default mood ?
				if ((m_fDefaultMoodTime < 0.0f) && (m_pCurrMood && (!m_pCurrMood->bPlaySingle)) && m_bEnableEventProcessing)
					EnterDefaultMood();					// ...yes
			}
			else
			{
				// one of the above criteria failed, so lets see if we can choose the new mood
				// do we have a new mood ?
				if (pNewMood)
				{
					// ...yes
					bool bEnterNewMood=true;
					// do we also have a default mood ?
					if (pDefMood && pNewMood)
					{
						// ...yes; does the default mood has a higher priority than the new one ?
						if (pDefMood->nPriority>pNewMood->nPriority)
							bEnterNewMood=false;							// ...yes, so we dont enter the new mood
					}
					if (m_pCurrMood && pNewMood)
					{
						// If currently playing mood is Play Single and have higher priority, doesnt switch to new mood.
						if (m_pCurrMood->bPlaySingle && m_pCurrMood->nPriority > pNewMood->nPriority)
							bEnterNewMood = false;
					}
					// should we enter the new mood ?
					if (bEnterNewMood && m_bEnableEventProcessing)
					{
						// ...yes
						SetMood(pNewMood);
						if (m_pCurrTheme)
							m_fDefaultMoodTime = m_pCurrTheme->fDefaultMoodTimeout;
					}
				}
			}
		}
	}

	{
		//CryAutoCriticalSection SmartCriticalSection12(m_CSMusic_PatternInstances);

		// Update all Decoders from MainThread
		TPatternInstancesVecItConst PatItEnd = m_PatternInstances.end();

		for (TPatternInstancesVecIt It=m_PatternInstances.begin(); It!=PatItEnd; ++It)
		{
			CMusicPatternInstance *pInstance = (*It);

			if (pInstance && pInstance->GetPattern() && pInstance->GetPattern()->GetDecoder())
			{
				pInstance->GetPattern()->GetDecoder()->MainThreadUpdate(CryGetCurrentThreadId());
			}
		}
	}


	// Remove all stopped patterns from PlayingPattren from MainThread.
	TPatternPlayInfoVec::const_iterator IterPlayingPatternEnd(m_vecPlayingPatterns.end());

	for (TPatternPlayInfoVec::iterator Iter(m_vecPlayingPatterns.begin()); Iter != IterPlayingPatternEnd; )
	{
		SMusicPatternPlayInfo const& PlayInfo = (*Iter);
		if (PlayInfo.ePlayingType == EPlaying_Stop)
		{
   			switch (PlayInfo.nLayer)
			{
			case MUSICLAYER_RHYTHMIC:
				{
					//CryAutoCriticalSection oLocalCS(m_CSMusic_PatternInstances);
					--m_nLayeredRhythmicPatterns;
					break;
				}
			case MUSICLAYER_INCIDENTAL:
				{
					//CryAutoCriticalSection oLocalCS(m_CSMusic_PatternInstances);
					--m_nLayeredIncidentalPatterns;
					break;
				}
			}
				
			// Make sure to remove this pattern from the tracks if they still sit there
			if (PlayInfo.pPatternInstance == m_pTrack1)
			{
				SetTrack1(NULL, MUSICLAYER_NONE);
			}
			else if (PlayInfo.pPatternInstance == m_pTrack2)
			{
				SetTrack2(NULL, MUSICLAYER_NONE);
			}
			else if (PlayInfo.pPatternInstance == m_pNextTrack)
			{
				SetNextTrack(NULL, MUSICLAYER_NONE);
			}

			Iter = m_vecPlayingPatterns.erase(Iter);
			IterPlayingPatternEnd = m_vecPlayingPatterns.end(); // renew end
		}
		else
		{
			// Special case handling in case there are playing patterns that are not stopped and we don't have valid m_pTrack1 and m_pTrack2.
			if ((PlayInfo.bForceTrack1 || PlayInfo.bForceTrack2) && !m_pTrack1 && !m_pTrack2 && !m_pNextTrack && PlayInfo.eBlendType != EBlend_FadeOut)
			{
				SetNextTrack(PlayInfo.pPatternInstance, PlayInfo.nLayer);
			}

			++Iter;
		}
	}

	bool const bNeedsStop = m_bPlaying && m_vecPlayingPatterns.empty() && m_vecQueuedPatterns.empty() && m_vecQueuedLoadPatterns.empty() && (!m_pCurrMood || m_pCurrMood->bStopped);
		
	if (bNeedsStop)
	{
		StopPlaying();

		// Here the last pattern stopped playing and no new one is queued, therefore clean the system.
		Silence();

		LogMsg(1, "<Music> bNeedsStop so StopPlaying() and Silence()");
	}

	if (m_pTrack1)
	{
		int const nPos                        = m_pTrack1->GetPos();
		uint32 const nNumSamples              = m_pTrack1->GetPattern()->GetNumSamples();
		uint32 const nSamplesToEnd            = (uint32)m_pTrack1->GetSamplesToEnd();
		float const fRemainingTimeInSeconds   = nSamplesToEnd / 44100.0f;
		float const fCurrentPlayTimeInSeconds = (nNumSamples - nSamplesToEnd) / 44100.0f;
		float const fProgress                 = (nNumSamples > 0) ? (nPos / (float)nNumSamples) : 1;

		m_oMusicSystemInfo.fMainPatternCurrentProgress            = fProgress;
		m_oMusicSystemInfo.fMainPatternPlayTimeInSeconds          = fCurrentPlayTimeInSeconds;
		m_oMusicSystemInfo.fMainPatternRemainingPlayTimeInSeconds = fRemainingTimeInSeconds;
	}

	QueueEvent(MUSICSYSTEM_EVENT_ON_UPDATE);

	// Now notify all our listeners
	SendEventsToListeners();

	m_CS.Unlock();
}

/* get system-status of music-system; returning pointer only valid until next call to GetStatus()
*/
SMusicSystemStatus* CMusicSystem::GetStatus()
{
	CryAutoCriticalSection SmartCriticalSection(m_CS);
	
	m_Status.bPlaying = (m_pChannel != NULL);
	m_Status.sTheme   = m_pCurrTheme ? m_pCurrTheme->sName.c_str() : "<none>";
	m_Status.sMood    = m_pCurrMood ? m_pCurrMood->sName.c_str() : "<none>";

	SPlayingPatternsStatus PatternStatus;

	CMusicPattern* pMusicPattern = NULL;
	TPatternPlayInfoVecItConst const IterPatternEnd(m_vecPlayingPatterns.end());

	for (TPatternPlayInfoVecItConst IterPattern(m_vecPlayingPatterns.begin()); IterPattern != IterPatternEnd; ++IterPattern)
	{
		SMusicPatternPlayInfo const& PlayInfo = (*IterPattern);
		pMusicPattern	= PlayInfo.pPatternInstance->GetPattern();

		if (pMusicPattern)
		{
			PatternStatus.nLayer							= PlayInfo.nLayer;
			PatternStatus.eBlendType					= PlayInfo.eBlendType;
			PatternStatus.nSamplePos					= PlayInfo.pPatternInstance->GetPos();
			PatternStatus.nSamplesToNextFade	= PlayInfo.pPatternInstance->GetSamplesToNextFadePoint();
			PatternStatus.nLength							= pMusicPattern->GetNumSamples();
			PatternStatus.nInstances					= pMusicPattern->GetNumInstances();
			PatternStatus.sPatternName				= pMusicPattern->GetName();
			PatternStatus.sFileName						= pMusicPattern->GetFilename();
			PatternStatus.fVolume							= pMusicPattern->GetLayeringVolume();
			PatternStatus.fPhase							= PlayInfo.fPhase;

			m_Status.m_vecStatusPlayingPatterns.push_back(PatternStatus);
		}
	}

	return &m_Status;
}

#ifdef INCLUDE_AUDIO_PRODUCTION_CODE
//////////////////////////////////////////////////////////////////////////
void CMusicSystem::UpdateDrawInfoInternal()
{
	if (m_CS.TryLock())
	{
		m_oDrawInfoInternal.nNumThemes = m_mapThemes.size();

		m_oDrawInfoInternal.nNumMoods = 0;
		TThemeMapItConst const IterEnd(m_mapThemes.end());

		for (TThemeMapItConst Iter(m_mapThemes.begin()); Iter != IterEnd; ++Iter)
		{
			m_oDrawInfoInternal.nNumMoods += (*Iter).second->mapMoods.size();
		}

		m_oDrawInfoInternal.nNumPatterns = m_mapPatterns.size();
		m_oDrawInfoInternal.nNumPatternInstances = m_PatternInstances.size();
		m_oDrawInfoInternal.nNumPatternInstancesRemovedFromStreamingThread = m_PatternInstancesRemovedFromStreamingThread.size();

		m_oDrawInfoInternal.sTheme = m_pCurrTheme ? m_pCurrTheme->sName.c_str() : "<none>";
		m_oDrawInfoInternal.sMood  = m_pCurrMood ? m_pCurrMood->sName.c_str() : "<none>";

		stl::free_container(m_oDrawInfoInternal.sTrack1Name);
		if (m_pTrack1 && m_pTrack1->GetPattern())
		{
			m_oDrawInfoInternal.sTrack1Name = m_pTrack1->GetPattern()->GetName();
		}

		stl::free_container(m_oDrawInfoInternal.sTrack2Name);
		if (m_pTrack2 && m_pTrack2->GetPattern())
		{
			m_oDrawInfoInternal.sTrack2Name = m_pTrack2->GetPattern()->GetName();
		}

		stl::free_container(m_oDrawInfoInternal.sNextTrackName);
		if (m_pNextTrack && m_pNextTrack->GetPattern())
		{
			m_oDrawInfoInternal.sNextTrackName = m_pNextTrack->GetPattern()->GetName();
		}

		// First clear the arrays.
		stl::free_container(m_oDrawInfoInternal.aoPlayingPatternInfos);
		stl::free_container(m_oDrawInfoInternal.aoQueuedPatterns);
		stl::free_container(m_oDrawInfoInternal.aoQueuedLoadPatterns);
		stl::free_container(m_oDrawInfoInternal.aoPatternInstances);
		stl::free_container(m_oDrawInfoInternal.aoPatternInstancesToBeRemoved);
		stl::free_container(m_oDrawInfoInternal.aoPatternInstancesRemovedFromStreamingThread);

		// Then update them.
		FillPatternPlayInfo(m_vecPlayingPatterns, m_oDrawInfoInternal.aoPlayingPatternInfos);
		FillPatternPlayInfo(m_vecQueuedPatterns, m_oDrawInfoInternal.aoQueuedPatterns);
		FillPatternPlayInfo(m_vecQueuedLoadPatterns, m_oDrawInfoInternal.aoQueuedLoadPatterns);
		FillPatternInstanceInfo(m_PatternInstances, m_oDrawInfoInternal.aoPatternInstances);
		FillPatternInstanceInfo(m_PatternInstancesToBeRemoved, m_oDrawInfoInternal.aoPatternInstancesToBeRemoved);
		FillPatternInstanceInfo(m_PatternInstancesRemovedFromStreamingThread, m_oDrawInfoInternal.aoPatternInstancesRemovedFromStreamingThread);

		m_CS.Unlock();
	}
}

//////////////////////////////////////////////////////////////////////////
void CMusicSystem::FillPatternPlayInfo(TPatternPlayInfoVec const& rPatternPlayInfos, TPatternInfoInternalVec& rPatternInfosInternal)
{
	float const fWhite[4]      = {1.0f, 1.0f, 1.0f, 1.0f};
	float const fGreen[4]      = {0.0f, 0.8f, 0.0f, 1.0f};
	float const fLightGreen[4] = {0.0f, 1.0f, 0.5f, 1.0f};
	float const fDarkGreen[4]  = {0.0f, 0.5f, 0.2f, 1.0f};
	float const fRed[4]        = {1.0f, 0.0f, 0.0f, 1.0f};
	float const fBlue[4]       = {0.0f, 0.0f, 1.0f, 1.0f};
	float const fCyan[4]       = {0.0f, 1.0f, 1.0f, 1.0f};

	TPatternPlayInfoVecItConst const ItEnd(rPatternPlayInfos.end());

	for (TPatternPlayInfoVecItConst It(rPatternPlayInfos.begin()); It != ItEnd; ++It)
	{
		SDrawInfoInternal::SPatternInfoInternal oPatternInfoInternal;
		SMusicPatternPlayInfo const& PlayInfo = (*It);
		CMusicPattern* const pMusicPattern = PlayInfo.pPatternInstance->GetPattern();

		switch(PlayInfo.nLayer)
		{
		case MUSICLAYER_MAIN:
			{
				memcpy(oPatternInfoInternal.fColor, fGreen, 4 * sizeof(float));
				oPatternInfoInternal.sLayer = "Main";

				break;
			}
		case MUSICLAYER_START:
			{
				memcpy(oPatternInfoInternal.fColor, fLightGreen, 4 * sizeof(float));
				oPatternInfoInternal.sLayer = "Start";

				break;
			}
		case MUSICLAYER_END:
			{
				memcpy(oPatternInfoInternal.fColor, fDarkGreen, 4 * sizeof(float));
				oPatternInfoInternal.sLayer = "End";

				break;
			}
		case MUSICLAYER_STINGER:
			{
				memcpy(oPatternInfoInternal.fColor, fRed, 4 * sizeof(float));
				oPatternInfoInternal.sLayer = "Stinger";

				break;
			}
		case MUSICLAYER_INCIDENTAL:
			{
				memcpy(oPatternInfoInternal.fColor, fCyan, 4 * sizeof(float));
				oPatternInfoInternal.sLayer = "Incidental";

				break;
			}
		case MUSICLAYER_RHYTHMIC:
			{
				memcpy(oPatternInfoInternal.fColor, fBlue, 4 * sizeof(float));
				oPatternInfoInternal.sLayer = "Rhythmic";

				break;
			}
		case MUSICLAYER_PATTERN_BY_NAME:
			{
				memcpy(oPatternInfoInternal.fColor, fBlue, 4 * sizeof(float));
				oPatternInfoInternal.sLayer = "Pattern by Name";

				break;
			}
		default:
			{
				memcpy(oPatternInfoInternal.fColor, fWhite, 4 * sizeof(float));
				oPatternInfoInternal.sLayer = "None";

				break;
			}
		}

		oPatternInfoInternal.sPatternName     = pMusicPattern->GetName();
		oPatternInfoInternal.sPatternFileName = pMusicPattern->GetFilename();
		oPatternInfoInternal.fVolume          = pMusicPattern->GetLayeringVolume();
		oPatternInfoInternal.fCurrentVolume   = PlayInfo.fPhase;
		oPatternInfoInternal.fColor[3]        = clamp_tpl(PlayInfo.fPhase, 0.2f, 1.0f);
		int32 const nPos                      = PlayInfo.pPatternInstance->GetPos();
		uint32 const nLength                  = pMusicPattern->GetNumSamples();
		oPatternInfoInternal.fProgress        = (nLength > 0) ? (nPos / (float)nLength) : 1.0f;
		oPatternInfoInternal.fNextFade        = (nLength > 0) ? ((nPos + PlayInfo.pPatternInstance->GetSamplesToNextFadePoint()) / (float)nLength) : 1.0f;

		rPatternInfosInternal.push_back(oPatternInfoInternal);
	}
}

//////////////////////////////////////////////////////////////////////////
void CMusicSystem::FillPatternInstanceInfo(TPatternInstancesVec const& rPatternInstancesInfos, TPatternInfoInternalVec& rPatternInfosInternal)
{
	float const fWhite[4] = {1.0f, 1.0f, 1.0f, 1.0f};
	float const fRed[4]   = {1.0f, 0.0f, 0.0f, 1.0f};
	float const fCyan[4]  = {0.0f, 1.0f, 1.0f, 1.0f};

	SMusicPatternFileInfo oMusicPatternFileInfo;
	TPatternInstancesVecItConst const IterEnd(rPatternInstancesInfos.end());

	for (TPatternInstancesVecItConst Iter(rPatternInstancesInfos.begin()); Iter != IterEnd; ++Iter)
	{
		CMusicPatternInstance* const pInstance = (*Iter);

		if (pInstance)
		{
			CMusicPattern* pMusicPattern = pInstance->GetPattern();

			if (pMusicPattern)
			{
				IMusicPatternDecoder* const pMusicPatternDecoder = pMusicPattern->GetDecoder();

				if (pMusicPatternDecoder)
				{
					SDrawInfoInternal::SPatternInfoInternal oPatternInfoInternal;

					pMusicPatternDecoder->GetFileInfo(oMusicPatternFileInfo);
					bool const bDecoderReady					= oMusicPatternFileInfo.eAudioFileOpenState == eAFOS_READY;
					bool const bDecoderInstanceReady	= pInstance->GetDecoderInstance() ? pInstance->GetDecoderInstance()->GetOpenState() == eAFOS_READY : false;
					bool const bReady									= bDecoderReady && bDecoderInstanceReady;
					bool const bAlmostReady						= (!bReady && bDecoderReady) || (!bReady && bDecoderInstanceReady);

					if (bReady)
					{
						memcpy(oPatternInfoInternal.fColor, fWhite, 4 * sizeof(float));
					}
					else
					{
						memcpy(oPatternInfoInternal.fColor, bAlmostReady ? fCyan : fRed, 4 * sizeof(float));
					}

					oPatternInfoInternal.sPatternName          = pMusicPattern->GetName();
					oPatternInfoInternal.sPatternFileName      = pMusicPattern->GetFilename();
					oPatternInfoInternal.nNumReferences        = pInstance->GetRefCount();
					oPatternInfoInternal.nNumInstances         = pMusicPattern->GetNumInstances();
					oPatternInfoInternal.nBufferFillPercentage = pInstance->GetBufferFillPercentage();

					rPatternInfosInternal.push_back(oPatternInfoInternal);
				}
			}
		}
	}
}
#endif // INCLUDE_AUDIO_PRODUCTION_CODE

//////////////////////////////////////////////////////////////////////////
bool CMusicSystem::IsPatternFading() const
{
	for (TPatternPlayInfoVecItConst VecIt = m_vecPlayingPatterns.begin(); VecIt != m_vecPlayingPatterns.end(); ++VecIt)
	{
		const SMusicPatternPlayInfo *pPlayInfo = &(*VecIt);
		if (pPlayInfo->eBlendType != EBlend_None)
			return true;
	}

	return false;
}

//////////////////////////////////////////////////////////////////////////

/* get system-status of music-system; returning pointer only valid until next call to GetStatus()
*/
void CMusicSystem::ClearStatus()
{
	stl::free_container(m_Status.m_vecStatusPlayingPatterns);
}

/* get pointer to mood-descriptor of current default mood (either theme specific or overriden default mood; whatever is currently set)
*/
SMusicMood* CMusicSystem::GetDefaultMood(SMusicTheme *pTheme) const
{
	CryAutoCriticalSection SmartCriticalSection(m_CS);

	if (!pTheme)
		return NULL;
	
	if (m_sDefaultMood.empty())
		return GetMood(pTheme, pTheme->sDefaultMood.c_str());
	else
	{
		SMusicMood *pMood = GetMood(pTheme, m_sDefaultMood.c_str());
	
		if (!pMood)
			return GetMood(pTheme, pTheme->sDefaultMood.c_str());		// return theme-specific mood if user defined default mood is not available
		else
			return pMood;
	}
}

/* enter the default mood (either theme specific or overriden default mood; whatever is currently set)
*/
void CMusicSystem::EnterDefaultMood()
{
	//CryAutoCriticalSection SmartCriticalSection(m_CSMusic_Data_ThemesAndMoodsPointers);

	if (m_pCurrTheme)
	{
		SetMood(GetDefaultMood(m_pCurrTheme));
		m_fDefaultMoodTime = -1.0f;
	}
	else
	{
		m_fDefaultMoodTime = 0.0f;	// no theme is set yet, so lets try it again in the next update...
	}
}

/* add a pattern to the play-list
*/
bool const CMusicSystem::PushPatternToMixList(SMusicPatternPlayInfo &PlayInfo)
{
	if (m_musicEnable)
	{
		// clean up pattern which are not updated through streaming callback because FMOD doesn't call it on Vol 0
		if (m_fMasterVolume == 0)
		{
			m_vecPlayingPatterns.clear();
		}

		LogMsg(2, "<Music> Pushing pattern %s in mix-list.", PlayInfo.pPatternInstance->GetPattern()->GetName());
		//MTRACE("Pushing pattern %s in mix-list.", PlayInfo.pPatternInstance->GetPattern()->GetName());

		// This is a newly requested pattern to play, so in case we still have pending loads transfer them to the
		// m_PatternInstancesToBeRemoved array and let the removal code handle this pattern instance
		if (!PlayInfo.bForceTrack1 && !PlayInfo.bForceTrack2)
		{
			size_t const nCount = m_vecQueuedPatterns.size();

			for (size_t i = 0; i < nCount; ++i)
			{
				m_PatternInstancesToBeRemoved.push_back(m_vecQueuedPatterns[i].pPatternInstance);
			}

			m_vecQueuedPatterns.clear();
		}

		// Make sure to queue patterns which's decoder and decoder instance haven't finished loading the audio data yet
		// or patterns that couldn't get pushed yet due to an already locked critical section
		IMusicPatternDecoderInstance const* const pMusicPatternDecoderInstance = PlayInfo.pPatternInstance->GetDecoderInstance();

		if (pMusicPatternDecoderInstance)
		{
#ifdef MUSIC_FORCE_QUEUED_TRACKS
			bool bQueuePattern = true;
#else
			bool bQueuePattern = false;
#endif // MUSIC_FORCE_QUEUED_TRACKS

			SMusicPatternFileInfo oMusicPatternFileInfo;
			PlayInfo.pPatternInstance->GetPattern()->GetDecoder()->GetFileInfo(oMusicPatternFileInfo);

			if (oMusicPatternFileInfo.eAudioFileOpenState == eAFOS_READY &&
				pMusicPatternDecoderInstance->GetOpenState() == eAFOS_READY &&
				!bQueuePattern)
			{
				if (PlayInfo.nStartFromPos != 0)
				{
					PlayInfo.pPatternInstance->SeekPos(PlayInfo.nStartFromPos);
				}

				m_vecPlayingPatterns.push_back(PlayInfo);

				return true;
			}
			else
			{
				m_vecQueuedPatterns.insert(m_vecQueuedPatterns.begin(), PlayInfo);
			}
		}
		else
		{
			gEnv->pSoundSystem->Log(eSLT_Error, "Could not push pattern %s to mix list! (decoder instance already deleted)", PlayInfo.pPatternInstance->GetPattern()->GetName());
			CRY_ASSERT_MESSAGE(0, "CMusicSystem::PushPatternToMixList() pattern decoder instance already deleted!");
		}
	}

	return false;
}

SMusicPatternPlayInfo* CMusicSystem::GetPlayingPatternInfo( const int nLayer, const EBlendingType BlendType )
{
	//CryAutoCriticalSection SmartCriticalSection(m_CSMusic_vecPlayingPatterns);
	TPatternPlayInfoVecIt ItEnd = m_vecPlayingPatterns.end();
	for (TPatternPlayInfoVecIt It=m_vecPlayingPatterns.begin(); It!=ItEnd; ++It)
	{
		SMusicPatternPlayInfo *pPlayInfo = &(*It);

		if ((pPlayInfo->nLayer & nLayer || nLayer == MUSICLAYER_ANY) && 
			(pPlayInfo->eBlendType == BlendType || BlendType == EBlend_Any))
			return pPlayInfo;

	}
	return NULL;

}

SMusicPatternPlayInfo* CMusicSystem::GetAnotherPlayingPatternInstance(CMusicPattern* pPattern, CMusicPatternInstance* thisPatternInstance)
{
	for(TPatternPlayInfoVecIt it = m_vecPlayingPatterns.begin(); it != m_vecPlayingPatterns.end(); ++it)
	{
		if(it->pPatternInstance != thisPatternInstance && it->pPatternInstance->GetPattern() == pPattern)
		{
			return &(*it);
		}
	}
	return NULL;
}

SMusicPatternPlayInfo* CMusicSystem::GetPlayingPatternInfo(TPatternInstancePtr pPattern, TPatternPlayInfoVec** pPatternVec)
{
	for(TPatternPlayInfoVecIt it = m_vecPlayingPatterns.begin(); it != m_vecPlayingPatterns.end(); ++it)
	{
		if(it->pPatternInstance == pPattern)
		{
			if(pPatternVec) *pPatternVec = &m_vecPlayingPatterns;
			return &(*it);
		}
	}

	for(TPatternPlayInfoVecIt it = m_vecQueuedPatterns.begin(); it != m_vecQueuedPatterns.end(); ++it)
	{
		if(it->pPatternInstance == pPattern)
		{
			if(pPatternVec) *pPatternVec = &m_vecQueuedPatterns;
			return &(*it);
		}
	}

	return NULL;
}

void CMusicSystem::CreatePlayInfoFromPattern(TPatternInstancePtr pPattern, SMusicPatternPlayInfo& PlayInfo)
{
	PlayInfo.nLayer						= MUSICLAYER_MAIN;
	PlayInfo.pPatternInstance = pPattern;
	PlayInfo.eBlendType				= EBlend_None;
	PlayInfo.ePlayingType			= EPlaying_Loop;
	PlayInfo.fPhase						= 1.0f;
}

bool CMusicSystem::IsPatternPlaying( const TPatternInstancePtr pPattern )
{
	//CryAutoCriticalSection SmartCriticalSection(m_CSMusic_vecPlayingPatterns);
	TPatternPlayInfoVecIt ItEnd = m_vecPlayingPatterns.end();
	for (TPatternPlayInfoVecIt It=m_vecPlayingPatterns.begin(); It!=ItEnd; ++It)
	{
		SMusicPatternPlayInfo *pPlayInfo = &(*It);

		if (pPlayInfo->pPatternInstance == pPattern)
			return true;

	}
	return false;

}



/* main streaming callback
	this is the main state-machine; check comments in function for details
	*/
void CMusicSystem::StreamingCallback(tAssetHandle pStream, void *pBuffer, int nLength)
{
	// Ignore if not playing, do this before acquiring a lock on m_CS to prevent a deadlock during shutdown.
	// FMOD Ex calls callback before m_pStream gets set, should not create a error.
	if (!m_bPlaying || !m_musicEnable || m_pStream == NULL)
		return;

	if (m_pStream != pStream)
	{
		// wrong stream; this should never happen
		CryFatalError( "<CrySound> (CMusicSystem::StreamingCallback) Wrong Stream" );
		return;
	}

	// Now we should be fine for mixing and reading data so let's acquire the lock
	CryAutoCriticalSection SmartCriticalSection(m_CS);

	FRAME_PROFILER( "Musicsystem::Callback",GetISystem(),PROFILE_MUSIC );
	gEnv->GetJobManager()->WaitForJob(g_MixJobState, 100/*ms timeout*/);

	//FUNCTION_PROFILER( gEnv->pSystem, PROFILE_SOUND );

	float fProfileTime0 = 0.0f;
	float fProfileTime1 = 0.0f;

	if (g_nMusicProfiling)
		fProfileTime0 = gEnv->pTimer->GetAsyncCurTime();

	signed short *pOutput = (signed short*)pBuffer;
	int nOfs = 0;
	int nSamples = nLength/m_nBytesPerSample;
	memset(&(pOutput[nOfs]), 0, nSamples*m_nBytesPerSample);

	if (!m_bWaitForQueuedPatterns)
	{
		size_t const nNumPlaying = m_vecPlayingPatterns.size();
		size_t const nNumQueued  = m_vecQueuedPatterns.size();

		if (nNumPlaying == 0 && nNumQueued > 0)
		{
			// Just return if we have nothing playing but are waiting for queued patterns.
			m_bWaitForQueuedPatterns = true;
			return;
		}

		{
			//CryAutoCriticalSection SmartCriticalSection2(m_CSMusic_Data_ThemesAndMoodsPointers);
			// check if theme and mood are valid
			if (((!m_pCurrTheme) || (!m_pCurrMood) || (!UpdateCurrentPatternSet(m_pCurrMood, nSamples, true))) && !m_pNextTrack && !nNumPlaying)
			{
				MixStreams(&(pOutput[nOfs]), nSamples);
				return;
			}
		}

		if (!m_pNextTrack && !nNumPlaying && !m_pTrack1)
		{
			SMusicPatternPlayInfo* pTempMain = GetPlayingPatternInfo( MUSICLAYER_END, EBlend_Any );

			if (!pTempMain && !nNumQueued)
			{
				SetNextTrack(ChoosePattern(m_pCurrMood, MUSICLAYER_MAIN), MUSICLAYER_MAIN);
			}

			MixStreams(&(pOutput[nOfs]), nSamples);
			return;
		}

		{
			// If there's a next track waiting and both tracks a free push it forward to track 1.
			if (!m_pTrack1 && !m_pTrack2 && m_pNextTrack)
			{
				SetTrack1(m_pNextTrack, m_NextTrackType);
				SetNextTrack(NULL, MUSICLAYER_NONE);

				if (!nNumPlaying)
				{
					MixStreams(&(pOutput[nOfs]), nSamples);

					if (!GetPlayingPatternInfo(m_pTrack1, NULL)) // it shouldn't exist yes, but just in case lets check, we don't want two instances playing
					{
						SMusicPatternPlayInfo PlayInfo;
						CreatePlayInfoFromPattern(m_pTrack1, PlayInfo);
						PushPatternToMixList(PlayInfo);
					}
				}

				return;
			}
		}

		TPatternInstancePtr pLocalTrack1 = NULL;
		int nLocalTrack1Type = MUSICLAYER_NONE;
		TPatternInstancePtr pLocalTrack2 = NULL;
		int nLocalTrack2Type = MUSICLAYER_NONE;
		TPatternInstancePtr pLocalNextTrack = NULL;
		int nLocalNextTrackType = MUSICLAYER_NONE;

		for (;;)
		{
			if (!GetPlayingPatternInfo(MUSICLAYER_ANY, EBlend_Any))
				break;
		
			{
				//CryAutoCriticalSection SmartCriticalSection6(m_CSMusic_Tracks);

				// sorting Patterns if Track1 is invalid and Track2 is fine
				if (!m_pTrack1 && m_pTrack2)
				{
					SetTrack1(m_pTrack2, m_Track2Type);
					SetTrack2(NULL, MUSICLAYER_NONE);
				}

				// if we dont have a valid Track1 now, we cannot continue
				if (!m_pTrack1)
				{
					MixStreams(&(pOutput[nOfs]), nSamples);

					return;
				}

				pLocalTrack1 = m_pTrack1;
				nLocalTrack1Type = m_Track1Type;
				pLocalTrack2 = m_pTrack2;
				nLocalTrack2Type = m_Track2Type;
				pLocalNextTrack = m_pNextTrack;
				nLocalNextTrackType = m_NextTrackType;
			}

			int nSamplesToNextFadePointOrEnd = pLocalTrack1->GetSamplesToNextFadePoint();

			{
				//CryAutoCriticalSection SmartCriticalSection7(m_CSMusic_Data_ThemesAndMoodsPointers);

				if ((!m_bBridging) && (nSamplesToNextFadePointOrEnd<=nSamples) && m_pCurrMood && m_pCurrMood->bMainSpawned )	
				{
					bool bFading = (GetPlayingPatternInfo(MUSICLAYER_MAIN, EBlend_FadeIn)	 || 
						GetPlayingPatternInfo(MUSICLAYER_MAIN, EBlend_FadeOut) ||
						GetPlayingPatternInfo(MUSICLAYER_END, EBlend_FadeOut));

					// ...yes, lets eventually choose some layered patterns
					// rhythmic layer
					if (	(!m_pCurrMood->pCurrPatternSet->vecRhythmicPatterns.empty()) &&
						(m_nLayeredRhythmicPatterns < m_pCurrMood->pCurrPatternSet->nMaxSimultaneousRhythmicPatterns) &&
						(m_nLayeredRhythmicPatterns+m_nLayeredIncidentalPatterns) < g_nMusicMaxPatterns &&
						!bFading)
					{
						// should we layer now ?
						uint32 const nRand = cry_rand32() % 100;
						bool bIsTimeForLayer = nRand < m_pCurrMood->pCurrPatternSet->fRhythmicLayerProbabilityInPercent;
						if (bIsTimeForLayer)
						{
							// ...yes
							CMusicPatternInstance *pLayeredPattern = ChoosePattern(m_pCurrMood, MUSICLAYER_RHYTHMIC);

							if (pLayeredPattern)
							{
								//CryAutoCriticalSection SmartCriticalSection8(m_CSMusic_PatternInstances);
								++m_nLayeredRhythmicPatterns;
								pLayeredPattern->Seek0(nSamplesToNextFadePointOrEnd);	// lets seek negative so it starts playing at the right time...
								// lets queue the new pattern so it gets automatically played till the end
								SMusicPatternPlayInfo PlayInfo;
								PlayInfo.nLayer						=	MUSICLAYER_RHYTHMIC;
								PlayInfo.pPatternInstance	=	pLayeredPattern;
								PlayInfo.eBlendType				=	EBlend_None;
								PlayInfo.ePlayingType			= EPlaying_Once;
								PlayInfo.fPhase						= 1.0f;
								PlayInfo.pRefCount				=	&m_nLayeredRhythmicPatterns;
								PlayInfo.nSamplesToWait		= 0;
								PlayInfo.fFadeTime				= m_pCurrMood->fFadeOutTime;
								PushPatternToMixList(PlayInfo);
	#ifdef _DEBUG
								SMusicPatternFileInfo FileInfo;
								pLayeredPattern->GetPattern()->GetFileInfo(FileInfo);
								LogMsg(3, "<Music> Layering rhythmic-layer %s", FileInfo.sFileName.c_str());
	#endif
							}
						}
					}

					// incidental layer
					if (	(!m_pCurrMood->pCurrPatternSet->vecIncidentalPatterns.empty()) && 
						(m_nLayeredIncidentalPatterns < m_pCurrMood->pCurrPatternSet->nMaxSimultaneousIncidentalPatterns) &&
						(m_nLayeredRhythmicPatterns+m_nLayeredIncidentalPatterns) < g_nMusicMaxPatterns &&
						!bFading)
					{
						// should we layer now ?
						uint32 const nRand = cry_rand32() % 100;
						bool bIsTimeForLayer = nRand < m_pCurrMood->pCurrPatternSet->fIncidentalLayerProbabilityInPercent;
						if (bIsTimeForLayer)
						{
							// ...yes
							CMusicPatternInstance *pLayeredPattern = ChoosePattern(m_pCurrMood, MUSICLAYER_INCIDENTAL);

							if (pLayeredPattern)
							{
								//CryAutoCriticalSection SmartCriticalSection9(m_CSMusic_PatternInstances);
								++m_nLayeredIncidentalPatterns;
								pLayeredPattern->Seek0(nSamplesToNextFadePointOrEnd);	// lets seek negative so it starts playing at the right time...
								// lets queue the new pattern so it gets automatically played till the end
								SMusicPatternPlayInfo PlayInfo;
								PlayInfo.nLayer						=	MUSICLAYER_INCIDENTAL;
								PlayInfo.pPatternInstance	=	pLayeredPattern;
								PlayInfo.eBlendType				=	EBlend_None;
								PlayInfo.ePlayingType			= EPlaying_Once;
								PlayInfo.fPhase						= 1.0f;
								PlayInfo.pRefCount				=	&m_nLayeredIncidentalPatterns;
								PlayInfo.nSamplesToWait		= 0;
								PlayInfo.fFadeTime				= m_pCurrMood->fFadeOutTime;
								PushPatternToMixList(PlayInfo);
	#ifdef _DEBUG
								SMusicPatternFileInfo FileInfo;
								pLayeredPattern->GetPattern()->GetFileInfo(FileInfo);
								LogMsg(3, "<Music> Layering incidental-layer %s", FileInfo.sFileName.c_str());
	#endif
							}
						}
					}
				}
			} // end of CryAutoCriticalSection SmartCriticalSection(m_CSMusic_Data_ThemesAndMoodsPointers);

			// new streaming code
			int nSamplesToNextFadePoint = pLocalTrack1->GetSamplesToNextFadePoint();
			int nSamplesToEnd						= pLocalTrack1->GetSamplesToEnd();
			int nCurrentSamplePos				= pLocalTrack1->GetPos();

			// If Track1 Pattern is not ready wait until it is
			if (nSamplesToNextFadePoint < 0 || nSamplesToEnd < 0 || nCurrentSamplePos < 0)
			{
				MixStreams(&(pOutput[nOfs]), nSamples);
				return;
			}

			//assert (pLocalNextTrack != NULL);
			if (pLocalNextTrack && !pLocalNextTrack->GetPattern())
			{
				assert(!"prevent a bug that should not happen");

				if (pLocalNextTrack)
				{
					//CryAutoCriticalSection SmartCriticalSection10(m_CSMusic_PatternInstancesRemovedFromStreamingThread);
					m_PatternInstancesRemovedFromStreamingThread.push_back(pLocalNextTrack);
				}

				pLocalNextTrack = NULL;
				return;
			}

			int nPreFadeSamples					= pLocalNextTrack ? pLocalNextTrack->GetPattern()->GetPreFadeIn() : 0;
			int nSamplesToPreFadeIn			= max(0, nSamplesToNextFadePoint - nPreFadeSamples );
			SMusicPatternPlayInfo *pTrack1PlayInfo = NULL;

			{
				//CryAutoCriticalSection SmartCriticalSection11(m_CSMusic_vecPlayingPatterns);

				TPatternPlayInfoVecIt ItEnd = m_vecPlayingPatterns.end();
				for (TPatternPlayInfoVecIt It=m_vecPlayingPatterns.begin();It!=ItEnd;++It)
				{
					SMusicPatternPlayInfo *pPlayInfo = &(*It);

					if (pLocalTrack1 == pPlayInfo->pPatternInstance)
						pTrack1PlayInfo = pPlayInfo;

					switch(pPlayInfo->eBlendType)
					{
					case EBlend_FadeInOnFadePoint:
						{
							if (nSamplesToPreFadeIn <= nSamples || pLocalTrack1 == pPlayInfo->pPatternInstance)
							{
								//if (!pLocalNextTrack->m_bPlayFromStart)
								{
									// Go to the position
									// prepare the m_pPatternMixBuffer with the correct number of samples to read
									SMusicPatternFileInfo FileInfoNext;
									pPlayInfo->pPatternInstance->GetPattern()->GetFileInfo(FileInfoNext);
									if (FileInfoNext.nSamples > 0)
									{
										int nSamplesToRead = nCurrentSamplePos % FileInfoNext.nSamples;

										// try if that decoder can seek directly to the position
										if (!pPlayInfo->pPatternInstance->SeekPos(nSamplesToRead))
										{
											// if not, do a fallback and read/seek to the position
											int nSamplesTurn = nSamples;
											while (nSamplesToRead > 0) 
											{
												//CryAutoCriticalSection SmartCriticalSection12(m_CSMusic_MixBuffers);
												pPlayInfo->pPatternInstance->GetPCMData((signed long*)m_pPatternMixBuffer, nSamplesTurn);
												nSamplesToRead -= nSamples;
												nSamplesTurn = min(nSamplesToRead, nSamplesTurn);
											}
										}
									}

								}

								if (pPlayInfo != pTrack1PlayInfo)
									pPlayInfo->nSamplesToWait = nSamplesToPreFadeIn;

								pPlayInfo->eBlendType = EBlend_FadeIn;
							}

							break;
						}
					case EBlend_FadeOut:
						{
							if (pLocalTrack1 == pPlayInfo->pPatternInstance)
							{
								if (pLocalTrack1)
								{
									//CryAutoCriticalSection SmartCriticalSection13(m_CSMusic_PatternInstancesRemovedFromStreamingThread);
									m_PatternInstancesRemovedFromStreamingThread.push_back(pLocalTrack1);
								}

								pLocalTrack1 = NULL;
							}
							break;
						}
					case EBlend_FadeOutOnFadePoint:
						{
							if (nSamplesToNextFadePoint <= nSamples)// || pLocalTrack1 == pPlayInfo->pPatternInstance)
							{
								pPlayInfo->eBlendType = EBlend_FadeOut;

								if (pLocalTrack1 == pPlayInfo->pPatternInstance)
								{
									if (pLocalTrack1)
									{
										//CryAutoCriticalSection SmartCriticalSection14(m_CSMusic_PatternInstancesRemovedFromStreamingThread);
										m_PatternInstancesRemovedFromStreamingThread.push_back(pLocalTrack1);
									}

									pLocalTrack1 = NULL;
								}
							}

							break;
						}
					case EBlend_FadeOutOnMainEnd:
						{
							if (nSamplesToEnd <= nSamples)// || pLocalTrack1 == pPlayInfo->pPatternInstance)
							{
								pPlayInfo->eBlendType = EBlend_FadeOut;

								if (pLocalTrack1 == pPlayInfo->pPatternInstance)
								{
									if (pLocalTrack1)
										m_PatternInstancesRemovedFromStreamingThread.push_back(pLocalTrack1);

									pLocalTrack1 = NULL;
								}
							}

							break;
						}
					case EBlend_PlayOnFadePoint:
						{
							if (nSamplesToNextFadePoint <= nSamples || pLocalTrack1 == pPlayInfo->pPatternInstance)
							{
								pPlayInfo->nSamplesToWait = nSamplesToNextFadePoint;
								pPlayInfo->eBlendType = EBlend_None;
							}

							break;
						}
					case EBlend_EndOnFadePoint:
						{
							if (nSamplesToNextFadePoint <= nSamples)
							{
								pPlayInfo->eBlendType = EBlend_None;
								pPlayInfo->ePlayingType = EPlaying_Stop;
							}

							break;
						}
					case EBlend_None:
						{
							if (pLocalTrack1 == pPlayInfo->pPatternInstance)
							{
								// speed up any pattern that is waiting for itself
								pPlayInfo->nSamplesToWait = 0;
							}

							break;
						}
					default:
						break;
					}

				}

			}

			// pLocalTrack1 was not found in the playing pattern, lets free it and get a new one 
			if (!pTrack1PlayInfo)
			{
				if (pLocalTrack1)
				{
					//CryAutoCriticalSection SmartCriticalSection15(m_CSMusic_PatternInstancesRemovedFromStreamingThread);
					m_PatternInstancesRemovedFromStreamingThread.push_back(pLocalTrack1);
				}

				pLocalTrack1 = NULL;
			}

			// start new pattern on Track1 if its looping
			if (pLocalNextTrack && ((nSamplesToEnd <= nSamples) || (nSamplesToNextFadePoint <= nSamples && pLocalNextTrack->m_bPlayAtOnce)) && m_pCurrMood && pTrack1PlayInfo && pTrack1PlayInfo->eBlendType != EBlend_FadeIn)
			{
				// start playing the new pattern

				// check if 2nd track is free
				if (pLocalTrack2 && pLocalTrack2->GetSamplesToEnd() > nSamples)
					break;

				if (!( (pLocalNextTrack->GetMood() == m_pCurrMood) || (pLocalNextTrack->GetMood() == m_pNextMood) ))
				{
					if (pLocalNextTrack)
					{
						//CryAutoCriticalSection SmartCriticalSection16(m_CSMusic_PatternInstancesRemovedFromStreamingThread);
						m_PatternInstancesRemovedFromStreamingThread.push_back(pLocalNextTrack);
					}

					{
						//CryAutoCriticalSection SmartCriticalSection17(m_CSMusic_Data_ThemesAndMoodsPointers);
						// next track isnt spawned by current mood, so lets get a new next track
						pLocalNextTrack = ChoosePattern(m_pCurrMood, MUSICLAYER_MAIN);
					}
				}

				if (!pLocalNextTrack->m_bPlayFromStart)
				{
					// Go to the position
					// prepare the m_pPatternMixBuffer with the correct number of samples to read
					SMusicPatternFileInfo FileInfoNext;
					pLocalNextTrack->GetPattern()->GetFileInfo(FileInfoNext);
					if (FileInfoNext.nSamples > 0)
					{
						int nSamplesToRead = nCurrentSamplePos % FileInfoNext.nSamples;

						// try if that decoder can seek directly to the position
						if (!pLocalNextTrack->SeekPos(nSamplesToRead))
						{
							// if not, do a fallback and read/seek to the position
							int nSamplesTurn = nSamples;
							while (nSamplesToRead > 0) 
							{
								//CryAutoCriticalSection SmartCriticalSection18(m_CSMusic_MixBuffers);
								pLocalNextTrack->GetPCMData((signed long*)m_pPatternMixBuffer, nSamplesTurn);
								nSamplesToRead -= nSamples;
								nSamplesTurn = min(nSamplesToRead, nSamplesTurn);
							}
						}
					}

					//m_pNextPattern->Seek0(-(nCurrentPos));
				}

				{
					SMusicPatternPlayInfo PlayInfo;
					PlayInfo.nLayer						=	nLocalNextTrackType;
					PlayInfo.pPatternInstance	=	pLocalNextTrack;

					{
						//CryAutoCriticalSection SmartCriticalSection19(m_CSMusic_Data_ThemesAndMoodsPointers);
						PlayInfo.fFadeTime			= m_pCurrMood->fFadeOutTime;
					}

					PlayInfo.fPhase						= pTrack1PlayInfo?pTrack1PlayInfo->fPhase:1.0f;

					if (nLocalNextTrackType == MUSICLAYER_MAIN)
						PlayInfo.ePlayingType			= EPlaying_Loop;
					else
						PlayInfo.ePlayingType			= EPlaying_Once;

					if (pLocalNextTrack->m_bPlayFromStart)
					{
						PlayInfo.eBlendType				=	pTrack1PlayInfo?pTrack1PlayInfo->eBlendType:EBlend_None;
					}
					else
					{
						PlayInfo.eBlendType				=	EBlend_FadeIn;
						PlayInfo.fPhase						= 0.0f;
					}

					PlayInfo.nSamplesToWait	= nSamplesToNextFadePoint;

					if (PushPatternToMixList(PlayInfo))
					{
						// Make sure a possible already existing track #2 is removed
						if (pLocalTrack2)
						{
							//CryAutoCriticalSection SmartCriticalSection20(m_CSMusic_PatternInstancesRemovedFromStreamingThread);
							m_PatternInstancesRemovedFromStreamingThread.push_back(pLocalTrack2);
						}

						// Assign the new pattern to track #2
						pLocalTrack2					= pLocalNextTrack;
						nLocalTrack2Type			= nLocalNextTrackType;
						//pLocalNextTrack				= NULL;
						//nLocalNextTrackType		= 0;
						m_bForcePatternChange	= false;
					}
					else
					{
						// This shouldn't happen but if it does then this is a TODO:
						// Handle proper removal of a possibly already existing track #2 once the new pattern has finished loading
						// For now just remove it anyway
						assert(0);

						if (pLocalTrack2)
						{
							//CryAutoCriticalSection SmartCriticalSection20(m_CSMusic_PatternInstancesRemovedFromStreamingThread);
							m_PatternInstancesRemovedFromStreamingThread.push_back(pLocalTrack2);
						}
					}
				}

				{
					//CryAutoCriticalSection SmartCriticalSection21(m_CSMusic_Data_ThemesAndMoodsPointers);
					m_pCurrMood->bPlayFromStart = true;
				}
			}

			break;
		} // end of CryAutoCriticalSection SmartCriticalSection(m_CSMusic_Tracks);

		SetTrack1(pLocalTrack1, nLocalTrack1Type);
		SetTrack2(pLocalTrack2, nLocalTrack2Type);
		SetNextTrack(pLocalNextTrack, nLocalNextTrackType);
	}

	float fMixProfileTime0 = 0.0f;
	float fMixProfileTime1 = 0.0f;

	if (g_nMusicProfiling)
		fMixProfileTime0 = gEnv->pTimer->GetAsyncCurTime();

	MixStreams(&(pOutput[nOfs]), nSamples);	// mix queued patterns

	if (g_nMusicProfiling)
	{
		fMixProfileTime1 = gEnv->pTimer->GetAsyncCurTime();
		CryLog(" Profile: Mix function %f", (fMixProfileTime1-fMixProfileTime0)*1000.0f);

		fProfileTime1 = gEnv->pTimer->GetAsyncCurTime();
		CryLog(" Profile: Frame %d Whole Streaming Callback %f", gEnv->pRenderer->GetFrameID(), (fProfileTime1-fProfileTime0)*1000.0f);
	}
}

void CMusicSystem::MixStreamsStreamEnds(SMusicPatternPlayInfo & PlayInfo)
{
	TPatternInstancePtr pushBackPtrs[8];
	int pushBackPtrsCnt = -1;

	unsigned int nTempLayer = PlayInfo.nLayer;
	int bTempPlaySingle = PlayInfo.pPatternInstance->m_bPlaySingle;
	TPatternInstancePtr pTempPattern = PlayInfo.pPatternInstance;

	if (pTempPattern)
		pushBackPtrs[++pushBackPtrsCnt] = pTempPattern;

	{
		//CryAutoCriticalSection SmartCriticalSection(m_CSMusic_Tracks);

		switch (nTempLayer)
		{
		case MUSICLAYER_MAIN:
			{
				// if the pattern was fading before it could fade out, lets restart it and continue the fade
				if (((PlayInfo.eBlendType == EBlend_FadeIn && PlayInfo.fPhase < 1.0f) ||
					(PlayInfo.eBlendType == EBlend_FadeOut && PlayInfo.fPhase > 0.0f)) && PlayInfo.ePlayingType != EPlaying_Stop )
				{
					PlayInfo.pPatternInstance->Seek0();
				}
				else
				{
					if (PlayInfo.ePlayingType == EPlaying_Stop)
						break;

					PlayInfo.ePlayingType = EPlaying_Stop;

					if (m_pNextTrack && m_pTrack1 && m_pNextTrack->GetMood() == m_pTrack1->GetMood())
					{			
						pushBackPtrs[++pushBackPtrsCnt] = m_pNextTrack;
						SetNextTrack(ChoosePattern(m_pCurrMood, MUSICLAYER_MAIN), MUSICLAYER_MAIN);
					}

					if (m_pTrack1 == pTempPattern)
					{
						if (m_pTrack1)
							pushBackPtrs[++pushBackPtrsCnt] = m_pTrack1;

						SetTrack1(NULL, MUSICLAYER_NONE);
					}
					if (m_pTrack2 == pTempPattern)
					{
						if (m_pTrack2)
							pushBackPtrs[++pushBackPtrsCnt] = m_pTrack2;

						SetTrack2(NULL, MUSICLAYER_NONE);
					}

					if (bTempPlaySingle)
						EnterDefaultMood();
				}

				break;
			}
		case MUSICLAYER_START:
			{
				if (PlayInfo.ePlayingType == EPlaying_Stop)
					break;

				PlayInfo.ePlayingType = EPlaying_Stop;

				if (m_pTrack1 == pTempPattern)
				{
					if (m_pTrack1)
						pushBackPtrs[++pushBackPtrsCnt] = m_pTrack1;

					SetTrack1(NULL, MUSICLAYER_NONE);
				}
				if (m_pTrack2 == pTempPattern)
				{
					if (m_pTrack2)
						pushBackPtrs[++pushBackPtrsCnt] = m_pTrack2;

					SetTrack2(NULL, MUSICLAYER_NONE);
				}
				break;
			}
		case MUSICLAYER_END:
			{
				{
					PlayInfo.ePlayingType = EPlaying_Stop;

					if (m_pTrack1 == pTempPattern)
					{
						if (m_pTrack1)
							pushBackPtrs[++pushBackPtrsCnt] = m_pTrack1;

						SetTrack1(NULL, MUSICLAYER_NONE);
					}
					if (m_pTrack2 == pTempPattern)
					{
						if (m_pTrack2)
							pushBackPtrs[++pushBackPtrsCnt] = m_pTrack2;

						SetTrack2(NULL, MUSICLAYER_NONE);
					}

					break;
				}

			}
		default:
			{
				if (m_pTrack1 == pTempPattern)
				{
					if (m_pTrack1)
						pushBackPtrs[++pushBackPtrsCnt] = m_pTrack1;

					SetTrack1(NULL, MUSICLAYER_NONE);
				}
				if (m_pTrack2 == pTempPattern)
				{
					if (m_pTrack2)
						pushBackPtrs[++pushBackPtrsCnt] = m_pTrack2;

					SetTrack2(NULL, MUSICLAYER_NONE);
				}

				PlayInfo.ePlayingType = EPlaying_Stop;

				break;
			}
		}

	} // end of CryAutoCriticalSection SmartCriticalSection(m_CSMusic_Tracks);

	pTempPattern = NULL;

	//reduce spu sim time by using only a single push_back
	for(int pb = 0; pb <= pushBackPtrsCnt; ++pb)
	{
		//CryAutoCriticalSection SmartCriticalSection(m_CSMusic_PatternInstancesRemovedFromStreamingThread);
		m_PatternInstancesRemovedFromStreamingThread.push_back(pushBackPtrs[pb]);
	}
}

/* mix all streams in the play-list into pBuffer; mixlength is nSamples
the data is processed before mixing if needed (eg. ramping)
*/
void CMusicSystem::MixStreams(void *const __restrict pBuffer, int nSamples)
{
	//initialize mixing buffers (already done for spu as it resides in bss and called once per job
//#ifdef _DEBUG
	// TODO INVESTIGATE : without this we have stuttering in level load
	//CryAutoCriticalSection SmartCriticalSection1(m_CSMusic_MixBuffers); // for the whole function and MixStreamsKernel
	memset(m_pPatternMixBuffer, 0, nSamples*m_nBytesPerSample*MixBufferCount);
//#endif
	m_MixStreamsPlayInfosCnt = 0;

	{
		int nMixBufferCounter = -1;
		size_t const nSize    = m_vecPlayingPatterns.size();

		for (size_t i = 0; i < nSize && i < MixBufferCount; ++i)
		{
			SMusicPatternPlayInfo &PlayInfo = m_vecPlayingPatterns[i];
			int nSamplesToRead							= nSamples;
			bool bStreamEnd									= false;

			if (!( PlayInfo.pPatternInstance ) || !( PlayInfo.pPatternInstance->GetDecoderInstance() ) || !( PlayInfo.pPatternInstance->GetDecoderInstance()->Ready() ) )
			{
				continue;
			}

			if (PlayInfo.ePlayingType == EPlaying_Stop)
			{
				bStreamEnd = true;
			}
			else
			{
				// Filter out pattern that are on hold.
				switch (PlayInfo.eBlendType)
				{
				case EBlend_FadeInOnFadePoint:
					{
						continue;
						break;
					}
				case EBlend_PlayOnFadePoint:
					{
						continue;
						break;
					}
				}

				// Now it's safe to increase the mix buffer counter!
				++nMixBufferCounter;

				int const nLength = PlayInfo.pPatternInstance->GetSamplesToEnd();

				if (nSamplesToRead >= nLength)
					nSamplesToRead = nLength;

				nSamplesToRead = max(0, nSamplesToRead - PlayInfo.nSamplesToWait);
				bool bLoop = PlayInfo.ePlayingType == EPlaying_Loop;

				// prepare the m_pPatternMixBuffer with the correct number of samples to read
				bool bGotData = PlayInfo.pPatternInstance->GetPCMData((signed long*)((char*)m_pPatternMixBuffer + nMixBufferCounter*MixBufferSize), nSamplesToRead, bLoop);

				if (nSamplesToRead > 0 && !bGotData)
				{
					// something went wrong in the decoder, lets stop that pattern
					bStreamEnd = true;
					PlayInfo.fPhase = 0.0f;
					PlayInfo.eBlendType = EBlend_FadeOut;
					PlayInfo.ePlayingType = EPlaying_Stop;
					break;
				}
				
				switch (PlayInfo.eBlendType)
				{
				case EBlend_FadeIn:
					{
						if (!FadeStreamIn(PlayInfo, (int16*)((char*)m_pPatternMixBuffer + nMixBufferCounter*MixBufferSize), (size_t)nSamplesToRead) || nSamplesToRead == 0)
						{
							TPatternPlayInfoVecItConst const It2End(m_vecPlayingPatterns.end());

							for (TPatternPlayInfoVecItConst It2(m_vecPlayingPatterns.begin()); It2 != It2End; ++It2)
							{
								SMusicPatternPlayInfo const& PlayInfo2 = (*It2);

								if ((PlayInfo2.pPatternInstance == m_pTrack1 || PlayInfo2.pPatternInstance == m_pTrack2) && PlayInfo2.pPatternInstance != PlayInfo.pPatternInstance)
								{
									break;
								}
							}

							if (nSamplesToRead == 0)
							{
								bStreamEnd = true;
							}
						}

						break;
					}
				case EBlend_FadeOut:
					{
						if (!FadeStreamOut(PlayInfo, (int16*)((char*)m_pPatternMixBuffer + nMixBufferCounter*MixBufferSize), (size_t)nSamplesToRead) || nSamplesToRead == 0)
						{
							bStreamEnd = true;
						}

						break;
					}
				}
				
				switch (PlayInfo.ePlayingType)
				{
				case EPlaying_Loop:
					{
						//CryAutoCriticalSection SmartCriticalSection4(m_CSMusic_Tracks);

						if (nSamplesToRead>=nLength)
						{
							bStreamEnd = true; // shouldnt go here
						}

						if (PlayInfo.pPatternInstance == m_pTrack1 && m_pNextTrack==0)
						{
							SetNextTrack(SPU_LOCAL_PTR(ChoosePattern(m_pCurrMood, MUSICLAYER_MAIN)), MUSICLAYER_MAIN);
						}
						break;
					}
				case EPlaying_Once:
					{
						if (nSamplesToRead>=nLength)
						{
							bStreamEnd = true;

							if (PlayInfo.nLayer == MUSICLAYER_PATTERN_BY_NAME)
							{
								CMusicPatternInstance const* const pMusicPatternInstance = PlayInfo.pPatternInstance;

								if (pMusicPatternInstance)
								{
									CMusicPattern const* const pMusicPattern = pMusicPatternInstance->GetPattern();

									if (pMusicPattern)
									{
										m_oMusicSystemInfo.sMusicPatternNameTrack1 = pMusicPattern->GetName();
									}
								}

								// We need a name for this event to be successful
								assert(!m_oMusicSystemInfo.sMusicPatternNameTrack1.empty());
								QueueEvent(MUSICSYSTEM_EVENT_ON_PATTERN_FINISHED);
							}
						}
						break;
					}
				}

				PlayInfo.nSamplesToWait = max(0, PlayInfo.nSamplesToWait - nSamples);
			}

			PushBackPlayInfo(PlayInfo, nSamplesToRead);

			if (bStreamEnd)
				MixStreamsStreamEnds(PlayInfo);

			if (!m_pTrack1 && !m_pTrack2)
			{
				int a = 0;	(void)a;
			}
		}
	}

		if(m_MixStreamsPlayInfosCnt > 0 )
		{
			TMixJob job(pBuffer, nSamples*m_nBytesPerSample,m_MixStreamsPlayInfosCnt,m_pPatternMixBuffer);
			job.SetClassInstance(this);
			job.RegisterJobState(&g_MixJobState);
			job.SetCacheMode(JobManager::SPUBackend::eCM_None);//no cache necessary
			job.Run();
		}
		else
		{
			MixStreamsKernel(pBuffer, nSamples*m_nBytesPerSample,m_MixStreamsPlayInfosCnt, m_pPatternMixBuffer);
		}
}

//////////////////////////////////////////////////////////////////////////
bool CMusicSystem::FadeStreamIn(SMusicPatternPlayInfo& PlayInfo, int16* pBuffer, size_t const nSamples) const
{
	// Ramping volume up for a pattern.
	float fLog               = 0.0f;
	float const fFadeTime   = (PlayInfo.fFadeTime != 0.0) ? PlayInfo.fFadeTime : m_fCurrCrossfadeTime;
	float const fPhaseAdder = (1.0f / static_cast<float>(m_nSampleRate)) / fFadeTime;

	// FADE_STREAM_UNROLL times unrolled.
	for (size_t i = 0; i < nSamples / FADE_STREAM_UNROLL; ++i)
	{
		PlayInfo.fPhase += fPhaseAdder * static_cast<float>(FADE_STREAM_UNROLL);

		if (PlayInfo.fPhase > 1.0f)
		{
			PlayInfo.fPhase     = 1.0f;
			PlayInfo.eBlendType = EBlend_None;

			return false;
		}

		// Quick logarithmic conversion.
		fLog = log_tpl(PlayInfo.fPhase * 1.71f + 1.0f);

		// Below we manipulate 32 samples at once.
		// This is equal to 0.00036 seconds as with a 44kHz stereo file each 4 byte sample represents 2 byte left channel + 2 byte right channel.

		// 1
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 2
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 3
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 4
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 5
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 6
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 7
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 8
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 9
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 10
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 11
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 12
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 13
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 14
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 15
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 16
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 17
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 18
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 19
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 20
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 21
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 22
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 23
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 24
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 25
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 26
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 27
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 28
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 29
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 30
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 31
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 32
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
	}

	// Do this once outside the loop and apply the same value to all remaining samples.
	PlayInfo.fPhase += fPhaseAdder * static_cast<float>(nSamples % FADE_STREAM_UNROLL);

	if (PlayInfo.fPhase > 1.0f)
	{
		PlayInfo.fPhase     = 1.0f;
		PlayInfo.eBlendType = EBlend_None;

		return false;
	}

	// Quick logarithmic conversion.
	fLog = log_tpl(PlayInfo.fPhase * 1.71f + 1.0f);

	// Process the remaining samples.
	for (size_t i = 0; i < nSamples % FADE_STREAM_UNROLL; ++i)
	{
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
	}
	
	return true;
}

//////////////////////////////////////////////////////////////////////////
bool CMusicSystem::FadeStreamOut(SMusicPatternPlayInfo& PlayInfo, int16* pBuffer, size_t const nSamples) const
{
	// Ramping volume down for a pattern.
	float fLog               = 0.0f;
	float const fFadeTime   = (PlayInfo.fFadeTime != 0.0) ? PlayInfo.fFadeTime : m_fCurrCrossfadeTime;
	float const fPhaseAdder = (1.0f / static_cast<float>(m_nSampleRate)) / fFadeTime;

	// FADE_STREAM_UNROLL times unrolled.
	for (size_t i = 0; i < nSamples / FADE_STREAM_UNROLL; ++i)
	{
		PlayInfo.fPhase -= fPhaseAdder * static_cast<float>(FADE_STREAM_UNROLL);

		if (PlayInfo.fPhase < 0.0f)
		{
			memset(pBuffer, 0, (nSamples - (i * FADE_STREAM_UNROLL)) * static_cast<size_t>(m_nBytesPerSample));
			PlayInfo.fPhase       = 0.0f;
			PlayInfo.ePlayingType = EPlaying_Stop;

			return false;
		}

		// Quick logarithmic conversion.
		fLog = log_tpl(PlayInfo.fPhase * 1.71f + 1.0f);

		// Below we manipulate 32 samples at once.
		// This is equal to 0.00036 seconds as with a 44kHz stereo file each 4 byte sample represents 2 byte left channel + 2 byte right channel.

		// 1
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 2
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 3
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 4
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 5
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 6
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 7
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 8
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 9
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 10
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 11
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 12
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 13
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 14
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 15
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 16
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 17
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 18
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 19
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 20
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 21
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 22
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 23
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 24
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 25
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 26
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 27
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 28
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 29
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 30
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 31
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;

		// 32
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
	}

	// Do this once outside the loop and apply the same value to all remaining samples.
	PlayInfo.fPhase -= fPhaseAdder * static_cast<float>(nSamples % FADE_STREAM_UNROLL);

	if (PlayInfo.fPhase < 0.0f)
	{
		memset(pBuffer, 0, (nSamples % FADE_STREAM_UNROLL) * static_cast<size_t>(m_nBytesPerSample));
		PlayInfo.fPhase       = 0.0f;
		PlayInfo.ePlayingType = EPlaying_Stop;

		return false;
	}

	// Quick logarithmic conversion.
	fLog = log_tpl(PlayInfo.fPhase * 1.71f + 1.0f);

	// Process the remaining samples.
	for (size_t i = 0; i < nSamples % FADE_STREAM_UNROLL; ++i)
	{
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
		*pBuffer = static_cast<int16>((static_cast<float>(*pBuffer)*fLog)); ++pBuffer;
	}

	return true;
}

/* all secondary layers (rhythmic, incidental) will be faded out over fFadeTime seconds
*/
void CMusicSystem::FadeOutAllSecondaryLayers(float const fFadeTime)
{
	//CryAutoCriticalSection SmartCriticalSection(m_CSMusic_vecPlayingPatterns);
	for (TPatternPlayInfoVecIt It=m_vecPlayingPatterns.begin(); It!=m_vecPlayingPatterns.end(); ++It)
	{
		SMusicPatternPlayInfo &PlayInfo=(*It);

		if (PlayInfo.ePlayingType == EPlaying_Stop)
			continue;

		if (PlayInfo.eBlendType == EBlend_FadeOut)
			continue;

		PlayInfo.eBlendType = EBlend_FadeOut;
		PlayInfo.fFadeTime = fFadeTime;
	}
}

/* create a new instance of pattern pszPattern
*/
CMusicPatternInstance* CMusicSystem::GetPatternInstance(const char *pszPattern)
{
	CryAutoCriticalSection SmartCriticalSection(m_CS);

	CMusicPatternInstance *pInstance = NULL;
	{
		//CryAutoCriticalSection SmartCriticalSection(m_CSMusic_Data_mapPatterns);
		TPatternMapItConst PatternIt = m_mapPatterns.find(CONST_TEMP_STRING(pszPattern));
	
		if (PatternIt == m_mapPatterns.end())
		{
			LogMsg(1, "[Warning] <Music> Pattern %s could not be found !", pszPattern);
			return NULL;
		}

		CMusicPattern *pPattern = PatternIt->second;
		pInstance = pPattern->CreateInstance();
	}

	if (pInstance)
	{
		//CryAutoCriticalSection SmartCriticalSection(m_CSMusic_PatternInstances);
		m_PatternInstances.push_back(pInstance); // store it to prevent other threads to call destructor
	}

	return pInstance;
}

/* check if a new pattern-set needs to be chosen
*/
bool CMusicSystem::UpdateCurrentPatternSet(SMusicMood *pMood, int nSamples, bool bAllowChange)
{
	CryAutoCriticalSection SmartCriticalSection(m_CS);
	
	if (!pMood)
		return false;
	
	
	pMood->fCurrPatternSetTime += (float)nSamples / (float)m_nSampleRate;	// update timing
	
	if (bAllowChange)
	{
		bool bChooseNewPatternSet = ((pMood->pCurrPatternSet == NULL) || (pMood->fCurrPatternSetTime >= pMood->fCurrPatternSetTimeout));

		if (bChooseNewPatternSet)
		{
			// yes... lets choose a new pattern-set
			ChoosePatternSet(pMood, !pMood->bPlayFromStart);
			return (pMood->pCurrPatternSet!=NULL);
		}
	}
	return true;
}

/* choose a new pattern-set and calc its timeout
*/
bool CMusicSystem::ChoosePatternSet(SMusicMood *pMood, const bool bUseSameIndex)
{
	CryAutoCriticalSection SmartCriticalSection(m_CS);
	
	if (!pMood)
		return false;
	
	SMusicPatternSet *pPrevPatternSet = pMood->pCurrPatternSet;
	int nPatternSets = (int)pMood->vecPatternSets.size();
	SMusicPatternSet *pPatternSet = NULL;
	int nCount = 1;

	switch (nPatternSets)
	{
		case 0:
			LogMsg(1, "[Warning] <Music> Choosing NULL pattern-set.");
			return false;
		case 1:
			pPatternSet = *(pMood->vecPatternSets.begin());
			pMood->nPatternRandIndex = 1;
			break;
		default:
			{
				//CryAutoCriticalSection SmartCriticalSection(m_CSMusic_Data_ThemesAndMoodsPointers);

				if (bUseSameIndex && m_pCurrMood)
					pMood->nPatternRandIndex = m_pCurrMood->nPatternRandIndex;
				else
					pMood->nPatternRandIndex = cry_rand32() % nPatternSets;
			}

			TPatternSetVecIt ItEnd = pMood->vecPatternSets.end();
			for (TPatternSetVecIt It=pMood->vecPatternSets.begin(); It!=ItEnd; ++It)
			{
				if (nCount == pMood->nPatternRandIndex)
				{
					pPatternSet = (*It);
					while (pPatternSet == pPrevPatternSet)	// make sure we select a different set
					{
						++It;
						nCount++;
						if (It==pMood->vecPatternSets.end())
						{
							It = pMood->vecPatternSets.begin();
							nCount = 0;
						}
						pPatternSet = (*It);
					}
					break;
				}
				nCount++;
			}

			break;
	}

	assert (pPatternSet);
	if (!pPatternSet)
		return false;

	pMood->fCurrPatternSetTime = 0.0f;
	pMood->fCurrPatternSetTimeout = pPatternSet->fMinTimeout + cry_frand() * (pPatternSet->fMaxTimeout - pPatternSet->fMinTimeout);
	LogMsg(1, "<Music> Choosing pattern-set %d with a timeout of %3.1f seconds.", nCount, pMood->fCurrPatternSetTimeout);
	pMood->pCurrPatternSet = pPatternSet;

	// we have to choose a new next pattern in order to reflect the new pattern set
	//m_pNextPattern=ChoosePattern(pMood, MUSICLAYER_MAIN);
	//m_NextPatternType = MUSICLAYER_MAIN;

	return true;
}

/* return bridge from pCurrTheme to pNewTheme; NULL if no such bridge exist
*/
CMusicPatternInstance* CMusicSystem::ChooseBridge(SMusicTheme *pCurrTheme, SMusicTheme *pNewTheme)
{
	CryAutoCriticalSection SmartCriticalSection(m_CS);

	if ((!pCurrTheme) || (!pNewTheme))
		return NULL;
	
	TThemeBridgeMapIt BridgeIt = pCurrTheme->mapBridges.find(pNewTheme->sName);
	
	if (BridgeIt==pCurrTheme->mapBridges.end())
		return NULL;	// no bridge defined
	
	CMusicPatternInstance *pBridgePattern = GetPatternInstance(BridgeIt->second.c_str());
	
	if (pBridgePattern)
		m_bBridging = true;	// set bridging flag
	
	//if (m_bBridging)
		//MTRACE("Setting bridging-flag.");

	return pBridgePattern;
}

/* choose a pattern and return its instance for pMood in nLayer
*/
CMusicPatternInstance* CMusicSystem::ChoosePattern(SMusicMood *pMood, int nLayer)
{
	if (!pMood)
		return NULL;

	if (!pMood->pCurrPatternSet)
	{
		LogMsg(1, "<Music> ChoosePattern failed due to NULL pCurrPatternSet! (Mood: %s)", pMood->sName.c_str());
		return NULL;
	}

	if (m_mapPatterns.empty())
		return NULL;

	TPatternDefVec *pPatternInfo = NULL;
	float fTotalProbability;
	bool bPlayAtOnce = true;
	bool bSpawnPattern = !pMood->bStopped;

	switch (nLayer)
	{
		default:
		case MUSICLAYER_MAIN:
			if (m_bBridging)
			{
				//MTRACE("Returning bridging pattern blindly...");
				assert(m_pNextTrack != 0);
				if(m_pNextTrack)
					return m_pNextTrack;	// if we're bridging we return the "NextPattern" since we want to continue the bridging
			}
			//if (!m_pCurrPattern && !pMood->pCurrPatternSet->vecStartPatterns.empty())
			//{
			//	// if no pattern is playing and start is available
			//	pPatternInfo=&(pMood->pCurrPatternSet->vecStartPatterns);
			//	fTotalProbability=0;
			//}
			//else
			//{
				pPatternInfo = &(pMood->pCurrPatternSet->vecMainPatterns);
				fTotalProbability = pMood->pCurrPatternSet->fTotalMainPatternProbability;
				bPlayAtOnce = false;
//			}
			break;
		case MUSICLAYER_RHYTHMIC:
			pPatternInfo = &(pMood->pCurrPatternSet->vecRhythmicPatterns);
			fTotalProbability = pMood->pCurrPatternSet->fTotalRhythmicPatternProbability;
			break;
		case MUSICLAYER_INCIDENTAL:
			pPatternInfo = &(pMood->pCurrPatternSet->vecIncidentalPatterns);
			fTotalProbability = pMood->pCurrPatternSet->fTotalIncidentalPatternProbability;
			break;
		case MUSICLAYER_STINGER:
			pPatternInfo = &(pMood->pCurrPatternSet->vecStingerPatterns);
			fTotalProbability = pMood->pCurrPatternSet->fTotalStingerPatternProbability;
			break;
		case MUSICLAYER_START:
			pPatternInfo = &(pMood->pCurrPatternSet->vecStartPatterns);
			fTotalProbability = 0;
			break;
		case MUSICLAYER_END:
			pPatternInfo = &(pMood->pCurrPatternSet->vecEndPatterns);
			fTotalProbability = 0;
			bPlayAtOnce = false;
			bSpawnPattern = true;
			break;
	}
	if ((!pPatternInfo) || (pPatternInfo->empty()) || !bSpawnPattern)
		return NULL;	// no pattern could be found in the requested layer
	// lets choose one accourding to their probability
//	float fProb = fTotalProbability;
	float const fRand = cry_frand() * fTotalProbability;
	fTotalProbability = 0.0f;

	const char *sPattern = "";

	// iterate through patterns and retrieve the one which was chosen by fTotalProbability
	TPatternDefVecIt InfoIt;
	for (InfoIt=pPatternInfo->begin();InfoIt!=pPatternInfo->end();++InfoIt)
	{
		SPatternDef *pPattern = (*InfoIt);
		fTotalProbability += pPattern->fProbabilityPoints;
		if (fRand <= fTotalProbability)
		{
			sPattern = pPattern->sName.c_str();
			break;
		}
	}

	assert( sPattern[0] );

		// make sure we do not choose a pattern which is already played
	// this whole section should depend on an option which can be added to layer properties
	if (nLayer == MUSICLAYER_INCIDENTAL || nLayer == MUSICLAYER_RHYTHMIC)
	{
		bool bPatternAlreadySpawned;
		int nTries = 0;
		do
		{
			// Check if a instance with the same name is already playing
			bPatternAlreadySpawned = false;
			{
				//CryAutoCriticalSection SmartCriticalSection(m_CSMusic_vecPlayingPatterns);
				for (TPatternPlayInfoVecIt It=m_vecPlayingPatterns.begin(); It!=m_vecPlayingPatterns.end(); ++It)
				{
					SMusicPatternPlayInfo &Info=(*It);
					if (stricmp(Info.pPatternInstance->GetPattern()->GetName(), sPattern )==0)	// in use already
					{
						bPatternAlreadySpawned = true;
						break;
					}
				}
			}


			// if the pattern is in used, iterate through the vector of pattern of this layer in the patternset
			// and find a pattern which is free, or return NULL to prevent the same pattern is selected
			if (bPatternAlreadySpawned)
			{
				InfoIt++;
				if (InfoIt == pPatternInfo->end())
					InfoIt = pPatternInfo->begin();
				
				SPatternDef *pPattern = (*InfoIt);
				sPattern = pPattern->sName.c_str();
				nTries++;

				if (nTries == (int)pPatternInfo->size())
					return NULL;	// nothing found
			}
		}while(bPatternAlreadySpawned);
	}

	CMusicPatternInstance *pInstance = GetPatternInstance( sPattern );

	if (pInstance)
	{
		//pInstance->m_bPlayFromStart = pMood->bPlayFromStart;
		pInstance->m_bPlaySingle = pMood->bPlaySingle;
		pInstance->m_bPlayAtOnce = bPlayAtOnce;
		pInstance->SetMood(pMood);
		pMood->bMainSpawned = pMood->bMainSpawned || (nLayer == MUSICLAYER_MAIN);
	}

	return pInstance;
}

//////////////////////////////////////////////////////////////////////////
void CMusicSystem::GetMemoryUsage(class ICrySizer* pSizer) const
{
	if (!pSizer->Add(*this))
		return;

	{
		//CryAutoCriticalSection SmartCriticalSection1(m_CSMusic_MixBuffers);
		if (m_pMixBuffer)
			pSizer->AddObject(m_pMixBuffer, MixBufferSize);
		if (m_pPatternMixBuffer)
			pSizer->AddObject(m_pPatternMixBuffer, MixBufferSize*MixBufferCount);
	}
	
	{
		SIZER_COMPONENT_NAME(pSizer, "Patterns");
		//CryAutoCriticalSection SmartCriticalSection2(m_CSMusic_Data_mapPatterns);
		TPatternMapItConst ItEnd = m_mapPatterns.end();
		for (TPatternMapItConst PatternIt=m_mapPatterns.begin(); PatternIt!=ItEnd; ++PatternIt)
		{
			CMusicPattern *pPattern = PatternIt->second;
			pPattern->GetMemoryUsage(pSizer);
		}

		//CryAutoCriticalSection SmartCriticalSection3(m_CSMusic_vecPlayingPatterns);
		TPatternPlayInfoVecItConst ItEnd2 = m_vecPlayingPatterns.end();
		for (TPatternPlayInfoVecItConst PatternIt2=m_vecPlayingPatterns.begin(); PatternIt2!=ItEnd2; ++PatternIt2)
		{
			const SMusicPatternPlayInfo &PlayInfo = (*PatternIt2);
			//PlayInfo.pPatternInstance->GetPattern()->GetMemoryUsage(pSizer);
			PlayInfo.pPatternInstance->GetMemoryUsage(pSizer);
		}

	}

	{
		//SIZER_COMPONENT_NAME(pSizer, "Meta Data");
		//CryAutoCriticalSection SmartCriticalSection4(m_CSMusic_Data_ThemesAndMoods);
		TThemeMapItConst ItEnd3 = m_mapThemes.end();
		for (TThemeMapItConst ThemeIt=m_mapThemes.begin(); ThemeIt!=ItEnd3; ++ThemeIt)
		{
			SMusicTheme *pTheme = ThemeIt->second;
			pSizer->AddObject(pTheme, sizeof(SMusicTheme));

			for (TMoodMapItConst MoodIt=pTheme->mapMoods.begin(); MoodIt!=pTheme->mapMoods.end(); ++MoodIt)
			{
				SMusicMood *pMood = MoodIt->second;
				pSizer->AddObject(pMood, sizeof(SMusicMood));
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CMusicSystem::RenamePattern( const char *sOldName,const char *sNewName )
{
	//CryAutoCriticalSection SmartCriticalSection(m_CSMusic_Data_mapPatterns);
	CMusicPattern *pPattern = stl::find_in_map( m_mapPatterns, sOldName, (CMusicPattern *)NULL);
	if (pPattern)
	{
		m_mapPatterns.erase(sOldName);
		m_mapPatterns[sNewName] = pPattern; // TODO: Double check whether pPattern is still valid after .erase()!
		pPattern->SetName(sNewName);
	}
}

//////////////////////////////////////////////////////////////////////////
void CMusicSystem::UpdatePatternDef( SPatternDef *pPatternDef )
{
	CryAutoCriticalSection SmartCriticalSection(m_CS);
	//CryAutoCriticalSection SmartCriticalSection1(m_CSMusic_Data_mapPatterns);

	// Update pattern.
	CMusicPattern *pPattern = stl::find_in_map( m_mapPatterns, pPatternDef->sName.c_str(), (CMusicPattern *)NULL);
	
	if (pPattern)
	{
		if (!m_bPlaying) // only update if music was stopped before
		{
			// Update existing pattern.
			if (stricmp(pPatternDef->sFilename.c_str(),pPattern->GetFilename()) != 0)
			{
				LogMsg(1, "[Warning] <Music> Pattern %s already found with references to different file name. Better stop music.", pPatternDef->sName.c_str());
				pPattern->SetFilename( pPatternDef->sFilename.c_str() );
			}
			pPattern->ClearFadePoints();

			pPattern->PrepareForMoreFadePoints(pPatternDef->vecFadePoints.size());
			for (TIntVecIt IntIt=pPatternDef->vecFadePoints.begin();IntIt!=pPatternDef->vecFadePoints.end();++IntIt)
			{
				pPattern->AddFadePoint(*IntIt);
			}
			pPattern->SetPreFadeIn(pPatternDef->nPreFadeIn);
			pPattern->SetLayeringVolume(pPatternDef->fLayeringVolume);
		}
	}
	else
	{
		// Add this pattern.
		pPattern = AddPattern(pPatternDef->sName.c_str(),pPatternDef->sFilename.c_str());
		if (pPattern)
		{
			pPattern->PrepareForMoreFadePoints(pPatternDef->vecFadePoints.size());
			for (TIntVecIt IntIt=pPatternDef->vecFadePoints.begin();IntIt!=pPatternDef->vecFadePoints.end();++IntIt)
			{
				pPattern->AddFadePoint(*IntIt);
			}
			pPattern->SetPreFadeIn(pPatternDef->nPreFadeIn);
			pPattern->SetLayeringVolume(pPatternDef->fLayeringVolume);
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CMusicSystem::UpdatePattern( SMusicInfo::Pattern *pPatternInfo )
{
	CryAutoCriticalSection SmartCriticalSection(m_CS);

	// Update pattern.
	CMusicPattern *pPattern = stl::find_in_map( m_mapPatterns,pPatternInfo->sName, (CMusicPattern *)NULL);
	if (pPattern)
	{
		// Only update stopped music
		if(m_bPlaying)
			StopPlaying();

		if (!m_bPlaying)
		{
			// Update existing pattern.
			if (stricmp(pPatternInfo->sFilename,pPattern->GetFilename()) != 0)
			{
				pPattern->SetFilename( pPatternInfo->sFilename );
				/*
				// Filename changed.
				const char *pszFilename = pPatternDef->sFilename.c_str();
				if (!pPattern->Open(pszFilename))
				{
				delete pPattern;
				LogMsg(1, "[Error] <Music> Cannot load music-pattern %s (%s) !", pPatternDef->sName.c_str(), pszFilename);
				return;
				}
				*/
			}
			pPattern->ClearFadePoints();
			pPattern->PrepareForMoreFadePoints(pPatternInfo->nFadePointsCount);
			for (int i = 0; i < pPatternInfo->nFadePointsCount; i++)
			{
				pPattern->AddFadePoint( pPatternInfo->pFadePoints[i] );
			}
			pPattern->SetPreFadeIn(pPatternInfo->nPreFadeIn);
			pPattern->SetLayeringVolume(pPatternInfo->fLayeringVolume);
		}
	}
	else
	{
		// Add this pattern.
		pPattern = AddPattern(pPatternInfo->sName,pPatternInfo->sFilename);
		if (pPattern)
		{
			pPattern->PrepareForMoreFadePoints(pPatternInfo->nFadePointsCount);
			for (int i = 0; i < pPatternInfo->nFadePointsCount; i++)
			{
				pPattern->AddFadePoint( pPatternInfo->pFadePoints[i] );
			}
			pPattern->SetPreFadeIn(pPatternInfo->nPreFadeIn);
			pPattern->SetLayeringVolume(pPatternInfo->fLayeringVolume);
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CMusicSystem::PlayPattern(char const* const sPattern, bool const bStopPrevious, bool const bPlaySynched, bool const bPlayOnTop)
{
	if (!m_musicEnable)
		return;

	if (!m_bForceQueuedRequests && m_CS.TryLock())
	{
		CMusicPatternInstance *pLayeredPattern = NULL;

		CMusicPattern *pPattern = stl::find_in_map( m_mapPatterns, CONST_TEMP_STRING(sPattern), (CMusicPattern *)NULL);

		if (pPattern)
		{
			// make sure music plays
			Pause(false);

			pLayeredPattern = pPattern->CreateInstance();
		}

		if (pLayeredPattern)
		{
			if (bStopPrevious)
			{
				for (TPatternPlayInfoVecIt It=m_vecPlayingPatterns.begin();It!=m_vecPlayingPatterns.end();++It)
				{
					SMusicPatternPlayInfo &PlayInfo = (*It);
					PlayInfo.eBlendType = EBlend_FadeOut;
				}

				m_pCurrTheme = NULL;
				m_pNextTheme = NULL;
				SetMood((SMusicMood*)NULL);
			}

			if (pLayeredPattern)
			{
				m_PatternInstances.push_back(pLayeredPattern); // store it to prevent other threads to call destructor
				m_nLayeredIncidentalPatterns++;

				// lets queue the new pattern so it gets automatically played till the end
				SMusicPatternPlayInfo PlayInfo;
				PlayInfo.nLayer           = bPlayOnTop ? MUSICLAYER_STINGER : MUSICLAYER_PATTERN_BY_NAME;
				PlayInfo.pPatternInstance = pLayeredPattern;
				PlayInfo.eBlendType       = EBlend_None;
				PlayInfo.ePlayingType     = EPlaying_Once;
				PlayInfo.fPhase           = 1.0f;
				PlayInfo.pRefCount        = &m_nLayeredIncidentalPatterns;

				if (m_pCurrMood)
					PlayInfo.fFadeTime = m_pCurrMood->fFadeOutTime;

				if (bPlaySynched)
				{
					int nSamplesToNextFadePoint = 0;

					if (m_pTrack2)
						nSamplesToNextFadePoint = m_pTrack2->GetSamplesToNextFadePoint();

					if (m_pTrack1)
						nSamplesToNextFadePoint = m_pTrack1->GetSamplesToNextFadePoint();

					PlayInfo.nSamplesToWait = nSamplesToNextFadePoint;
					PlayInfo.eBlendType = EBlend_PlayOnFadePoint;
				}

				if (PushPatternToMixList(PlayInfo) && !bPlayOnTop)
				{
					SetTrack2(PlayInfo.pPatternInstance, PlayInfo.nLayer);
				}
			}
		}

		m_CS.Unlock();
	}
	else
	{
		SMusicSystemQueuedRequest oQueuedRequest;
		oQueuedRequest.eMusicSystemQueudRequestType = eMSQRT_PlayPattern;
		oQueuedRequest.sName												= sPattern;
		oQueuedRequest.bStopPrevious								= bStopPrevious;
		oQueuedRequest.bPlaySynched									= bPlaySynched;
		oQueuedRequest.bPlayOnTop                   = bPlayOnTop;

		m_aoQueuedRequests.push_back(oQueuedRequest);
	}
}

//////////////////////////////////////////////////////////////////////////
bool CMusicSystem::StopPattern(char const* const sPatternNameToStop)
{
	if (sPatternNameToStop && sPatternNameToStop[0])
	{
		stack_string sPatternName;
		TPatternPlayInfoVecItConst const IterEnd(m_vecPlayingPatterns.end());

		for (TPatternPlayInfoVecIt Iter(m_vecPlayingPatterns.begin()); Iter != IterEnd; ++Iter)
		{
			SMusicPatternPlayInfo& roPlayInfo = (*Iter);
			
			CMusicPatternInstance const* const pMusicPatternInstance = roPlayInfo.pPatternInstance;

			if (pMusicPatternInstance)
			{
				CMusicPattern const* const pMusicPattern = pMusicPatternInstance->GetPattern();

				if (pMusicPattern)
				{
					sPatternName = pMusicPattern->GetName();

					if (sPatternName.compareNoCase(sPatternNameToStop) == 0)
					{
						roPlayInfo.ePlayingType = EPlaying_Stop;
						roPlayInfo.eBlendType   = EBlend_None;
						roPlayInfo.nLayer       = MUSICLAYER_NONE;
						QueueEvent(MUSICSYSTEM_EVENT_ON_PATTERN_STOP);

						return true;
					}
				}
			}
		}
	}

	return false;
}

//////////////////////////////////////////////////////////////////////////
void CMusicSystem::DeletePattern( const char *sPattern )
{
	//CryAutoCriticalSection SmartCriticalSection(m_CSMusic_Data_mapPatterns);
	m_mapPatterns.erase( sPattern );
}

//////////////////////////////////////////////////////////////////////////
char const* const CMusicSystem::GetPatternNameTrack1() const
{
	if (m_pTrack1)
	{
		CMusicPattern const* const pMusicPattern = m_pTrack1->GetPattern();

		if (pMusicPattern)
		{
			return pMusicPattern->GetName();
		}
	}

	return NULL;
}

//////////////////////////////////////////////////////////////////////////
bool CMusicSystem::PlayStart(EBlendingType eBlendType)
{
	if (!m_musicEnable)
		return false;

	if (!m_bForceQueuedRequests && m_CS.TryLock())
	{
		// pick one
		TPatternInstancePtr pNewPattern = NULL;
		{
			//CryAutoCriticalSection SmartCriticalSection(m_CSMusic_Data_ThemesAndMoodsPointers);
			pNewPattern = ChoosePattern(m_pCurrMood, MUSICLAYER_START);
		}

		if (pNewPattern)
		{
			// lets queue the new pattern so it gets automatically played till the end
			SMusicPatternPlayInfo PlayInfo;
			PlayInfo.nLayer						= MUSICLAYER_START;
			PlayInfo.pPatternInstance = pNewPattern;
			PlayInfo.eBlendType				= eBlendType;
			PlayInfo.ePlayingType			= EPlaying_Once;
			PlayInfo.fPhase						= 1.0f;

			if (PushPatternToMixList(PlayInfo))
			{
				SetTrack2(pNewPattern, PlayInfo.nLayer);
				m_bForcePatternChange	= false;
			}
			else
			{
				// Delay the mixing until the pattern has finished loading
				SetNextTrack(pNewPattern, PlayInfo.nLayer);
			}

			m_CS.Unlock();
			return true;
		}

		m_CS.Unlock();
		return false;
	}
	else
	{
		SMusicSystemQueuedRequest oQueuedRequest;
		oQueuedRequest.eMusicSystemQueudRequestType = eMSQRT_PlayStart;
		oQueuedRequest.eBlendingType								= eBlendType;

		m_aoQueuedRequests.push_back(oQueuedRequest);
	}

	// Return false as well if we needed to queue this request.
	return false;
}


//////////////////////////////////////////////////////////////////////////
void CMusicSystem::PlayStinger()
{
	if (!m_musicEnable)
		return;

	if (!m_bForceQueuedRequests && m_CS.TryLock())
	{
		if (!GetPlayingPatternInfo(MUSICLAYER_STINGER, EBlend_Any))
		{
			// pick one
			TPatternInstancePtr pNewPattern = NULL;
			{
				//CryAutoCriticalSection SmartCriticalSection(m_CSMusic_Data_ThemesAndMoodsPointers);
				pNewPattern = ChoosePattern(m_pCurrMood, MUSICLAYER_STINGER);
			}

			if (pNewPattern)
			{
				//m_nLayeredRhythmicPatterns++;
				//pLayeredPattern->Seek0(nSamplesToNextFade);	// lets seek negative so it starts playing at the right time...
				// lets queue the new pattern so it gets automatically played till the end
				SMusicPatternPlayInfo PlayInfo;
				PlayInfo.nLayer = MUSICLAYER_STINGER;
				PlayInfo.pPatternInstance = pNewPattern;
				PlayInfo.eBlendType				= EBlend_None;
				PlayInfo.ePlayingType			= EPlaying_Once;
				PlayInfo.fPhase						= 1.0f;
				PushPatternToMixList(PlayInfo);
			}
		}

		m_CS.Unlock();
	}
	else
	{
		SMusicSystemQueuedRequest oQueuedRequest;
		oQueuedRequest.eMusicSystemQueudRequestType = eMSQRT_PlayStinger;

		m_aoQueuedRequests.push_back(oQueuedRequest);
	}
}

//////////////////////////////////////////////////////////////////////////
void CMusicSystem::LoadPatternFromXML( XmlNodeRef node,SPatternDef *pPattern )
{
	// Loading.
	pPattern->sName = node->getAttr( "Name" );
	node->getAttr( "Probability",pPattern->fProbabilityPoints );
	node->getAttr( "LayeringVolume",pPattern->fLayeringVolume );
	pPattern->fLayeringVolume = min(1.0f, pPattern->fLayeringVolume);
	pPattern->sFilename = node->getAttr( "Filename" );
	node->getAttr( "PreFadeIn",pPattern->nPreFadeIn );
	
	const char *sFadePoints = node->getAttr( "FadePoints" );
	if (sFadePoints[0])
	{
		char sPoints[4096];
		strncpy( sPoints,sFadePoints,sizeof(sPoints) );
		sPoints[sizeof(sPoints)-1] = '\0';

		size_t commaCount = 0;
		for (const char* p = sPoints; *p; ++p)
			if (*p == ',') ++commaCount;
		pPattern->vecFadePoints.reserve(commaCount+1);
			
		char *token = strtok( sPoints,"," );
		while( token != NULL )
		{
			pPattern->vecFadePoints.push_back((size_t)atoi(token));
			token = strtok( NULL,"," );
		}
	}

	UpdatePatternDef(pPattern);
}

//////////////////////////////////////////////////////////////////////////
void CMusicSystem::LoadMoodFromXML( XmlNodeRef &node,SMusicMood *pMood )
{
	pMood->sName = node->getAttr( "Name" );
	node->getAttr( "PlaySingle",pMood->bPlaySingle );
	node->getAttr( "Priority",pMood->nPriority );
	node->getAttr( "FadeOutTime",pMood->fFadeOutTime );
	if (node->getChildCount() > 0)
	{
		// Save pattern sets.
		pMood->vecPatternSets.reserve(node->getChildCount());
		for (int i = 0; i < node->getChildCount(); i++)
		{
			XmlNodeRef nodePtrnSet = node->getChild(i);

			POOL_NEW_CREATE(SMusicPatternSet, pPatternSet);

			if (pPatternSet)
			{
				pMood->vecPatternSets.push_back(pPatternSet);

				nodePtrnSet->getAttr( "MaxTimeout",pPatternSet->fMaxTimeout );
				nodePtrnSet->getAttr( "MinTimeout",pPatternSet->fMinTimeout );
				nodePtrnSet->getAttr( "IncidentalLayerProbability",pPatternSet->fIncidentalLayerProbabilityInPercent );
				nodePtrnSet->getAttr( "RhythmicLayerProbability",pPatternSet->fRhythmicLayerProbabilityInPercent );
				nodePtrnSet->getAttr( "MaxSimultaneousIncidentalPatterns",pPatternSet->nMaxSimultaneousIncidentalPatterns );
				nodePtrnSet->getAttr( "MaxSimultaneousRhythmicPatterns",pPatternSet->nMaxSimultaneousRhythmicPatterns );

				// Save patterns.
				XmlNodeRef nodeMainLayer = nodePtrnSet->findChild( "MainLayer" );
				XmlNodeRef nodeRhythmicLayer = nodePtrnSet->findChild( "RhythmicLayer" );
				XmlNodeRef nodeIncidentalLayer = nodePtrnSet->findChild( "IncidentalLayer" );
				XmlNodeRef nodeStartLayer = nodePtrnSet->findChild( "StartLayer" );
				XmlNodeRef nodeEndLayer = nodePtrnSet->findChild( "EndLayer" );
				XmlNodeRef nodeStingerLayer = nodePtrnSet->findChild( "StingerLayer" );

				if (nodeMainLayer)
				{
					for (int j = 0; j < nodeMainLayer->getChildCount(); j++)
					{
						XmlNodeRef childNode = nodeMainLayer->getChild(j);
						POOL_NEW_CREATE(SPatternDef, pPattern);

						if (pPattern)
						{
							pPatternSet->vecMainPatterns.push_back(pPattern);
							LoadPatternFromXML( childNode,pPattern );
						}
					}
				}
				if (nodeRhythmicLayer)
				{
					for (int j = 0; j < nodeRhythmicLayer->getChildCount(); j++)
					{
						XmlNodeRef childNode = nodeRhythmicLayer->getChild(j);
						POOL_NEW_CREATE(SPatternDef, pPattern);

						if (pPattern)
						{
							pPatternSet->vecRhythmicPatterns.push_back(pPattern);
							LoadPatternFromXML( childNode,pPattern );
						}
					}
				}
				if (nodeIncidentalLayer)
				{
					for (int j = 0; j < nodeIncidentalLayer->getChildCount(); j++)
					{
						XmlNodeRef childNode = nodeIncidentalLayer->getChild(j);
						POOL_NEW_CREATE(SPatternDef, pPattern);

						if (pPattern)
						{
							pPatternSet->vecIncidentalPatterns.push_back(pPattern);
							LoadPatternFromXML( childNode,pPattern );
						}
					}
				}
				if (nodeStartLayer)
				{
					for (int j = 0; j < nodeStartLayer->getChildCount(); j++)
					{
						XmlNodeRef childNode = nodeStartLayer->getChild(j);
						POOL_NEW_CREATE(SPatternDef, pPattern);

						if (pPattern)
						{
							pPatternSet->vecStartPatterns.push_back(pPattern);
							LoadPatternFromXML( childNode,pPattern );
						}
					}
				}
				if (nodeEndLayer)
				{
					for (int j = 0; j < nodeEndLayer->getChildCount(); j++)
					{
						XmlNodeRef childNode = nodeEndLayer->getChild(j);
						POOL_NEW_CREATE(SPatternDef, pPattern);

						if (pPattern)
						{
							pPatternSet->vecEndPatterns.push_back(pPattern);
							LoadPatternFromXML( childNode,pPattern );
						}
					}
				}
				if (nodeStingerLayer)
				{
					for (int j = 0; j < nodeStingerLayer->getChildCount(); j++)
					{
						XmlNodeRef childNode = nodeStingerLayer->getChild(j);
						POOL_NEW_CREATE(SPatternDef, pPattern);

						if (pPattern)
						{
							pPatternSet->vecStingerPatterns.push_back(pPattern);
							LoadPatternFromXML( childNode,pPattern );
						}
					}
				}

				//////////////////////////////////////////////////////////////////////////
				pPatternSet->fTotalMainPatternProbability				= 0.0f;
				pPatternSet->fTotalRhythmicPatternProbability		= 0.0f;
				pPatternSet->fTotalIncidentalPatternProbability	= 0.0f;
				pPatternSet->fTotalStingerPatternProbability		= 0.0f;

				size_t const iSizeMain				= pPatternSet->vecMainPatterns.size();
				size_t const iSizeRhythmic		= pPatternSet->vecRhythmicPatterns.size();
				size_t const iSizeIncidental	= pPatternSet->vecIncidentalPatterns.size();
				size_t const iSizeStinger			= pPatternSet->vecStingerPatterns.size();

				for(size_t j = 0; j < iSizeMain; ++j)
				{
					pPatternSet->fTotalMainPatternProbability += pPatternSet->vecMainPatterns[j]->fProbabilityPoints;
				}
				for(size_t j = 0; j < iSizeRhythmic; ++j)
				{
					pPatternSet->fTotalRhythmicPatternProbability += pPatternSet->vecRhythmicPatterns[j]->fProbabilityPoints;
				}
				for(size_t j = 0; j < iSizeIncidental; ++j)
				{
					pPatternSet->fTotalIncidentalPatternProbability += pPatternSet->vecIncidentalPatterns[j]->fProbabilityPoints;
				}
				for(size_t j = 0; j < iSizeStinger; ++j)
				{
					pPatternSet->fTotalStingerPatternProbability += pPatternSet->vecStingerPatterns[j]->fProbabilityPoints;
				}
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CMusicSystem::LoadThemeFromXML( XmlNodeRef &node,SMusicTheme *pTheme )
{
	// Loading.
	pTheme->sName = node->getAttr( "Name" );
	pTheme->sDefaultMood = node->getAttr( "DefaultMood" );
	node->getAttr( "DefaultMoodTimeout",pTheme->fDefaultMoodTimeout );

	XmlNodeRef nodeMoods = node->findChild("Moods");
	if (nodeMoods)
	{
		for (int i = 0; i < nodeMoods->getChildCount(); i++)
		{
			XmlNodeRef childNode = nodeMoods->getChild(i);
			POOL_NEW_CREATE(SMusicMood, pMood);

			if (pMood)
			{
				LoadMoodFromXML( childNode,pMood );
				pTheme->mapMoods[pMood->sName.c_str()] = pMood;
			}
		}
	}

	XmlNodeRef nodeBridges = node->findChild( "Bridges" );
	if (nodeBridges)
	{
		for (int i = 0; i < nodeBridges->getChildCount(); i++)
		{
			XmlNodeRef nodeBridge = nodeBridges->getChild(i);
			const char *sPattern = nodeBridge->getAttr( "Pattern" );
			if (sPattern[0])
			{
				pTheme->mapBridges[nodeBridges->getTag()] = (const char*)sPattern;
			}
		}
	}	
}

//////////////////////////////////////////////////////////////////////////
bool CMusicSystem::LoadFromXML( const char *sFilename, bool bAddData, bool bReplaceData)
{
		LOADING_TIME_PROFILE_SECTION(GetISystem());
		MEMSTAT_CONTEXT(EMemStatContextTypes::MSC_Other, 0, "Music");

		XmlNodeRef root = gEnv->pSystem->LoadXmlFromFile( sFilename );
		if (!root)
		{
				gEnv->pSystem->Warning( VALIDATOR_MODULE_SYSTEM,VALIDATOR_WARNING,VALIDATOR_FLAG_SOUND,sFilename,
						"<Music> Description XML file %s failed to load",sFilename );
				return false;
		}
		return LoadFromXMLNode( root, bAddData, bReplaceData);
}

//////////////////////////////////////////////////////////////////////////
bool CMusicSystem::LoadFromXMLNode( const XmlNodeRef root, bool bAddData, bool bReplaceData)
{
  if (bReplaceData || !bAddData)
  {
    StopPlaying(); // this needs to be called outside of critical section because Stop internally calls a CS
    Silence();
  }

  CryAutoCriticalSection SmartCriticalSection(m_CS);

  // Loading.
  if (!bAddData)
  {
    Unload();
  }

	// Iterate music themes
	for (int i = 0; i < root->getChildCount(); i++)
	{
		XmlNodeRef nodeTheme = root->getChild(i);
		if (nodeTheme->isTag("Theme"))
		{
			//CryAutoCriticalSection SmartCriticalSection(m_CSMusic_Data_ThemesAndMoods);

			// Add this pattern to music data, if not already there
			TThemeMapIt It=m_mapThemes.find(CONST_TEMP_STRING(nodeTheme->getAttr( "Name" )));

			if( It == m_mapThemes.end() )
			{
				POOL_NEW_CREATE(SMusicTheme, pTheme);

				if (pTheme)
				{
					LoadThemeFromXML( nodeTheme, pTheme );
					m_mapThemes[pTheme->sName] = pTheme;	// theme not found
				}
			}
		}
	}

	m_bDataLoaded = true;

	// start playing the stream...
	//if (m_pChannel == NULL)
		//StartPlaying();

	return true;
}

#ifdef INCLUDE_AUDIO_PRODUCTION_CODE
//////////////////////////////////////////////////////////////////////////
void CMusicSystem::DrawInformation(IRenderer* const pRenderer, float xpos, float ypos)
{
	float const fWhite[4] = {1.0f, 1.0f, 1.0f, 1.0f};
	float const fGreen[4] = {0.0f, 0.8f, 0.0f, 1.0f};
	float const fRed[4]   = {1.0f, 0.0f, 0.0f, 1.0f};

	// If this method gets access to the critical section it will get new data otherwise it will use the previous one.
	UpdateDrawInfoInternal();

	ypos += 40.0f;

	pRenderer->Draw2dLabel(xpos, ypos, 1.5f, fWhite, false, "Themes: %d, Moods: %d, Patterns: %d ", m_oDrawInfoInternal.nNumThemes, m_oDrawInfoInternal.nNumMoods, m_oDrawInfoInternal.nNumPatterns);
	ypos += 15.0f;

	pRenderer->Draw2dLabel(xpos, ypos, 1.5f, fWhite, false, "Pattern Instances: %d, to be removed %d ", m_oDrawInfoInternal.nNumPatternInstances, m_oDrawInfoInternal.nNumPatternInstancesRemovedFromStreamingThread);
	ypos += 15.0f;

	pRenderer->Draw2dLabel(xpos, ypos, 1.5f, m_bPlaying ? fGreen : fRed, false, "Music is %s, %s %s", m_bPlaying ? "playing" : "not playing", m_bPause ? "paused" : "not paused", m_bForcePatternChange ? "with Force Pattern Change" : "");
	ypos += 15.0f;

	pRenderer->Draw2dLabel(xpos, ypos, 1.5f, fWhite, false, "Theme: %s Mood: %s", m_oDrawInfoInternal.sTheme.c_str(), m_oDrawInfoInternal.sMood.c_str());
	ypos += 15.0f;

	pRenderer->Draw2dLabel(xpos, ypos, 1.5f, fWhite, false, "Track 1: %s", m_oDrawInfoInternal.sTrack1Name.c_str());
	ypos += 15.0f;

	pRenderer->Draw2dLabel(xpos, ypos, 1.5f, fWhite, false, "Track 2: %s", m_oDrawInfoInternal.sTrack2Name.c_str());
	ypos += 15.0f;

	pRenderer->Draw2dLabel(xpos, ypos, 1.5f, fWhite, false, "Next Pattern: %s", m_oDrawInfoInternal.sNextTrackName.c_str());
	ypos += 30.0f;

	pRenderer->Draw2dLabel(xpos, ypos, 1.5f, fWhite, false, "Playing Patterns:");
	ypos += 15.0f;
	DrawInformationPlayInfoVec(pRenderer, xpos, ypos, m_oDrawInfoInternal.aoPlayingPatternInfos);

	if(g_nMusicInfoDebugFilter & eMIDF_PATTERNS || g_nMusicInfoDebugFilter & eMIDF_ALL)
	{
		pRenderer->Draw2dLabel(xpos, ypos, 1.5f, fWhite, false, "Queued Patterns:");
		ypos += 15.0f;
		DrawInformationPlayInfoVec(pRenderer, xpos, ypos, m_oDrawInfoInternal.aoQueuedPatterns);

		pRenderer->Draw2dLabel(xpos, ypos, 1.5f, fWhite, false, "Queued Load Patterns:");
		ypos += 15.0f;
		DrawInformationPlayInfoVec(pRenderer, xpos, ypos, m_oDrawInfoInternal.aoQueuedLoadPatterns);
	}

	pRenderer->Draw2dLabel(xpos, ypos, 1.5f, fWhite, false, "Pattern Instances:");
	ypos += 15.0f;
	DrawInformationPatternInstancesVec(pRenderer, xpos, ypos, m_oDrawInfoInternal.aoPatternInstances);

	if(g_nMusicInfoDebugFilter & eMIDF_INSTANCES || g_nMusicInfoDebugFilter & eMIDF_ALL)
	{
		pRenderer->Draw2dLabel(xpos, ypos, 1.5f, fWhite, false, "Pattern Instances To Be Removed:");
		ypos += 15.0f;
		DrawInformationPatternInstancesVec(pRenderer, xpos, ypos, m_oDrawInfoInternal.aoPatternInstancesToBeRemoved);

		pRenderer->Draw2dLabel(xpos, ypos, 1.5f, fWhite, false, "Pattern Instances To Be Removed From Steaming:");
		ypos += 15.0f;
		DrawInformationPatternInstancesVec(pRenderer, xpos, ypos, m_oDrawInfoInternal.aoPatternInstancesRemovedFromStreamingThread);
	}
}

//////////////////////////////////////////////////////////////////////////
void CMusicSystem::DrawInformationPlayInfoVec(IRenderer* pRenderer, float& xpos, float& ypos, TPatternInfoInternalVec const& raoPatternInfos)
{
	TPatternInfoInternalVec::const_iterator const IterEnd(raoPatternInfos.end());

	for (TPatternInfoInternalVec::const_iterator Iter(raoPatternInfos.begin()); Iter != IterEnd; ++Iter)
	{
		SDrawInfoInternal::SPatternInfoInternal const& rPatternInfo = (*Iter);
		pRenderer->Draw2dLabel(xpos, ypos, 1.5f, rPatternInfo.fColor, false, "%s: %s (%s) Volume: %1.2f [%1.2f] Progress: %1.2f NextFade: %1.2f",  rPatternInfo.sLayer.c_str(), rPatternInfo.sPatternName.c_str(), rPatternInfo.sPatternFileName.c_str(), rPatternInfo.fVolume * rPatternInfo.fCurrentVolume, rPatternInfo.fVolume, rPatternInfo.fProgress, rPatternInfo.fNextFade);
		ypos += 15.0f;
	}

	ypos += 15.0f;
}

//////////////////////////////////////////////////////////////////////////
void CMusicSystem::DrawInformationPatternInstancesVec(IRenderer* pRenderer, float& xpos, float& ypos, TPatternInfoInternalVec const& raoPatternInfos)
{
	TPatternInfoInternalVec::const_iterator const IterEnd(raoPatternInfos.end());

	for (TPatternInfoInternalVec::const_iterator Iter(raoPatternInfos.begin()); Iter != IterEnd; ++Iter)
	{
		SDrawInfoInternal::SPatternInfoInternal const& rPatternInfo = (*Iter);
		pRenderer->Draw2dLabel(xpos, ypos, 1.5f, rPatternInfo.fColor, false, "%s %s Ref: %d Instance: %d (%d%%)", rPatternInfo.sPatternFileName.c_str(), rPatternInfo.sPatternName.c_str(), rPatternInfo.nNumReferences, rPatternInfo.nNumInstances, rPatternInfo.nBufferFillPercentage);
		ypos += 15.0f;
	}

	ypos += 15.0f;
}

//////////////////////////////////////////////////////////////////////////
void CMusicSystem::LogMsg(const int nVerbosity, const char *pszFormat, ...)
{
	// Push log-message in internal list, so it can be output on next update-cycle.
	// This needs to be done due to multi-threading issues with the logging functions.
	if (g_nDebugMusic && g_nDebugMusic >= nVerbosity)
	{
		va_list ArgList;
		char szBuffer[2048];
		va_start(ArgList, pszFormat);
		vsprintf_s(szBuffer, pszFormat, ArgList);
		va_end(ArgList);
		m_vecLog.push_back(szBuffer);
	}
}

//////////////////////////////////////////////////////////////////////////
void CMusicSystem::FlushLog()
{
	// Flush all log-messages and delete internal list.
	if (g_nDebugMusic)
	{
		CryAutoCriticalSection SmartCriticalSection(m_CS);

		for (TStringVecItConst It=m_vecLog.begin();It!=m_vecLog.end();++It)
		{
			gEnv->pSoundSystem->Log(eSLT_Message, (*It).c_str());
		}
		m_vecLog.clear();
	}
}
#endif // INCLUDE_AUDIO_PRODUCTION_CODE

//////////////////////////////////////////////////////////////////////////
void CMusicSystem::Serialize(bool const bSave)
{
	IXmlSerializer* const pSerializer = gEnv->pSystem->GetXmlUtils()->CreateXmlSerializer();
	
	if (pSerializer)
	{
		ISerialize* pSer = NULL;

		if (bSave)
		{
			// Release the previous xml node to have a clean start.
			ReleaseXMLNode(m_oMusicStateNode);

			m_oMusicStateNode = gEnv->pSystem->CreateXmlNode("MusicState");
			pSer = pSerializer->GetWriter(m_oMusicStateNode);
		}
		else
		{
			pSer = pSerializer->GetReader(m_oMusicStateNode);
		}

		TSerialize ser = TSerialize(pSer);
		Serialize(ser);

		if (!bSave)
		{
			Pause(false);
		}

		pSerializer->Release();
	}
}

//////////////////////////////////////////////////////////////////////////
void CMusicSystem::Serialize(TSerialize ser)
{
	if (GetImpl(ser))
	{
		CryAutoCriticalSection SmartCriticalSection(m_CS);

		CryFixedStringT<512> sTempName;

		ser.Value("ForcePatternChange", m_bForcePatternChange);
		ser.Value("Pause", m_bPause);
		ser.Value("Enable", m_musicEnable);

		ser.Value("MasterVolume", m_fMasterVolume);
		ser.Value("DelayTheme", m_nDelayThemeInSec);

		if (ser.IsWriting())
		{
			ser.Value("Playing", m_bPlaying);

			// themes
			sTempName = m_pCurrTheme ? m_pCurrTheme->sName.c_str() : "";
			ser.Value("CurrTheme", sTempName);

			sTempName = m_pNextTheme ? m_pNextTheme->sName.c_str() : "";
			ser.Value("NextTheme", sTempName);

			// moods
			sTempName = m_pCurrMood ? m_pCurrMood->sName.c_str() : "";
			ser.Value("CurrMood", sTempName);
			sTempName = (m_pCurrMood && m_pCurrMood->pSpawnedFromTheme) ? m_pCurrMood->pSpawnedFromTheme->sName.c_str() : "";
			ser.Value("CurrMoodTheme", sTempName);

			if (m_pCurrMood)
			{
				ser.Value("CurrMoodStopped", m_pCurrMood->bStopped);
				ser.Value("CurrMoodMainSpawned", m_pCurrMood->bMainSpawned);
				ser.Value("CurrMoodRandIndex", m_pCurrMood->nPatternRandIndex);
			}

			sTempName = m_pNextMood ? m_pNextMood->sName.c_str() : "";
			ser.Value("NextMood", sTempName);
			sTempName = (m_pNextMood && m_pNextMood->pSpawnedFromTheme) ? m_pNextMood->pSpawnedFromTheme->sName.c_str() : "";
			ser.Value("NextMoodTheme", sTempName);

			if (m_pNextMood)
			{
				ser.Value("NextMoodStopped", m_pNextMood->bStopped);
			}

			ser.Value("DefaultMood", m_sDefaultMood);
			ser.Value("DefaultMoodTime", m_fDefaultMoodTime);

			int nNumPatterns = (int)(m_vecPlayingPatterns.size() + m_vecQueuedPatterns.size());

			for (int i = 0; i < 2; ++i)
			{
				TPatternPlayInfoVec& vec = i==0 ? m_vecPlayingPatterns : m_vecQueuedPatterns;

				TPatternPlayInfoVecIt ItEnd = vec.end();

				for (TPatternPlayInfoVecIt It = vec.begin(); It != ItEnd; ++It)
				{
					SMusicPatternPlayInfo *pPlayInfo = &(*It);

					// Use the temp string until the serializer can handle const pointers.
					sTempName = pPlayInfo->pPatternInstance->GetPattern()->GetName();

					// Pause menu shouldn't to be serialized.
					if (sTempName.compareNoCase("pause_menu") != 0)
					{
						ser.BeginGroup("Pattern");

						ser.Value("PatternName", sTempName.c_str());
						ser.Value("PatternMood",pPlayInfo->pPatternInstance->GetMood() ? pPlayInfo->pPatternInstance->GetMood()->sName : "");
						ser.Value("PatternTheme",pPlayInfo->pPatternInstance->GetMood() && pPlayInfo->pPatternInstance->GetMood()->pSpawnedFromTheme ? pPlayInfo->pPatternInstance->GetMood()->pSpawnedFromTheme->sName : "");

						ser.Value("Layer",pPlayInfo->nLayer);
						int nBlendType = (int)pPlayInfo->eBlendType;
						ser.Value("BlendType",nBlendType);
						int nPlayingType = (int)pPlayInfo->ePlayingType;
						ser.Value("PlayingType",nPlayingType);
						float fPhase = pPlayInfo->fPhase;
						ser.Value("Phase",fPhase);
						float fFadeTime = pPlayInfo->fFadeTime;
						ser.Value("FadeTime",fFadeTime);
						ser.Value("SamplesToWait",pPlayInfo->nSamplesToWait);
						ser.Value("FadeOutMainTracks",pPlayInfo->bFadeOutMainTracks);

						ser.Value("PlayFromStart",pPlayInfo->pPatternInstance->m_bPlayFromStart);
						ser.Value("PlaySingle",pPlayInfo->pPatternInstance->m_bPlaySingle);
						ser.Value("PlayAtonce",pPlayInfo->pPatternInstance->m_bPlayAtOnce);

						int nPos = pPlayInfo->pPatternInstance->GetPos();
						ser.Value("Pos",nPos);

						bool isTrack1 = (pPlayInfo->pPatternInstance == m_pTrack1) || (pPlayInfo->bForceTrack1);
						bool isTrack2 = (pPlayInfo->pPatternInstance == m_pTrack2) || (pPlayInfo->bForceTrack2);
						ser.Value("IsTrack1",isTrack1);
						ser.Value("IsTrack2",isTrack2);
						
						if (m_pNextTrack)
						{
							sTempName = m_pNextTrack->GetPattern()->GetName();
							ser.Value("NextTrackName", sTempName.c_str());
							ser.Value("NextTrackType", m_NextTrackType);
							ser.Value("NextTrackAtOnce", m_pNextTrack->m_bPlayAtOnce);
							ser.Value("NextTrackFromStart", m_pNextTrack->m_bPlayFromStart);
							ser.Value("NextTrackPlaySingle", m_pNextTrack->m_bPlaySingle);
							ser.Value("NextTrackMood",m_pNextTrack->GetMood() ? m_pNextTrack->GetMood()->sName : "");
							ser.Value("NextTrackTheme",m_pNextTrack->GetMood() && m_pNextTrack->GetMood()->pSpawnedFromTheme ? m_pNextTrack->GetMood()->pSpawnedFromTheme->sName : "");
						}
						
						ser.EndGroup();
					}
					else
					{
						--nNumPatterns; // remove pause menu from the number of patterns.
					}
				}
			}
			ser.Value("PatternCount", nNumPatterns);
		}
		else // Reading
		{
			Pause(true);

			// Flush all queued up tracks
			// m_PatternInstancesToBeRemoved array and let the removal code handle this pattern instance.
			TPatternPlayInfoVec::const_iterator const ItInfoEnd(m_vecQueuedPatterns.end());

			for (TPatternPlayInfoVec::const_iterator It(m_vecQueuedPatterns.begin()); It != ItInfoEnd; ++It)
			{
				m_PatternInstancesToBeRemoved.push_back((*It).pPatternInstance);
			}

			m_vecQueuedPatterns.clear();

			SetTrack1(NULL, MUSICLAYER_NONE);
			SetTrack2(NULL, MUSICLAYER_NONE);
			SetNextTrack(NULL, MUSICLAYER_NONE);

			ser.Value("NextTrackName", sTempName);

			if (!sTempName.empty())
			{
				CMusicPattern* const pNextTrack = stl::find_in_map(m_mapPatterns, CONST_TEMP_STRING(sTempName), (CMusicPattern*)NULL);

				if (pNextTrack)
				{
					CMusicPatternInstance* const pNextTrackInstance = pNextTrack->CreateInstance();

					if (pNextTrackInstance)
					{
						m_PatternInstances.push_back(pNextTrackInstance); // store it to prevent other threads to call destructor

						ser.Value("NextTrackType", m_NextTrackType);
						SetNextTrack(pNextTrackInstance, m_NextTrackType);

						ser.Value("NextTrackAtOnce", m_pNextTrack->m_bPlayAtOnce);
						ser.Value("NextTrackFromStart", m_pNextTrack->m_bPlayFromStart);
						ser.Value("NextTrackPlaySingle", m_pNextTrack->m_bPlaySingle);

						CryFixedStringT<32> sMoodName;
						CryFixedStringT<32> sThemeName;
						ser.Value("NextTrackMood",sMoodName);
						ser.Value("NextTrackTheme",sThemeName);

						if (!sThemeName.empty())
						{
							TThemeMapIt ThemeIt = m_mapThemes.find(CONST_TEMP_STRING(sThemeName));

							if (ThemeIt != m_mapThemes.end())
							{
								SMusicTheme* pTheme = ThemeIt->second;

								if (!sMoodName.empty())
								{
									TMoodMapIt MoodIt = pTheme->mapMoods.find(CONST_TEMP_STRING(sMoodName));

									if (MoodIt != pTheme->mapMoods.end())
									{
										m_pNextTrack->SetMood(MoodIt->second);
									}
								}
							}
						}
					}
				}
			}

			// themes
			ser.Value("CurrTheme", sTempName);
			TThemeMapIt ThemeIt = m_mapThemes.find(CONST_TEMP_STRING(sTempName));

			if (ThemeIt != m_mapThemes.end())
				m_pCurrTheme = ThemeIt->second;
			else
				m_pCurrTheme = NULL;

			ser.Value("NextTheme", sTempName);
			ThemeIt = m_mapThemes.find(CONST_TEMP_STRING(sTempName));

			if (ThemeIt != m_mapThemes.end())
				m_pNextTheme = ThemeIt->second;
			else
				m_pNextTheme = NULL;

			// moods
			m_pCurrMood = NULL;
			ser.Value("CurrMoodTheme", sTempName);
			ThemeIt = m_mapThemes.find(CONST_TEMP_STRING(sTempName));

			if (ThemeIt != m_mapThemes.end())
			{
				SMusicTheme* pTheme = ThemeIt->second;
				ser.Value("CurrMood", sTempName);

				TMoodMapIt MoodIt = pTheme->mapMoods.find(CONST_TEMP_STRING(sTempName));

				if (MoodIt != pTheme->mapMoods.end())
				{
					m_pCurrMood = MoodIt->second;
					ser.Value("CurrMoodStopped", m_pCurrMood->bStopped);
					ser.Value("CurrMoodMainSpawned", m_pCurrMood->bMainSpawned);
					ser.Value("CurrMoodRandIndex", m_pCurrMood->nPatternRandIndex);
				}
			}

			m_pNextMood = NULL;
			ser.Value("NextMoodTheme", sTempName);
			ThemeIt = m_mapThemes.find(CONST_TEMP_STRING(sTempName));

			if (ThemeIt != m_mapThemes.end())
			{
				SMusicTheme* pTheme = ThemeIt->second;
				ser.Value("NextMood", sTempName);

				TMoodMapIt MoodIt = pTheme->mapMoods.find(CONST_TEMP_STRING(sTempName));

				if (MoodIt!=pTheme->mapMoods.end())
				{
					m_pNextMood = MoodIt->second;
					ser.Value("NextMoodStopped", m_pNextMood->bStopped);
				}
			}

			ser.Value("DefaultMood", m_sDefaultMood);
			ser.Value("DefaultMoodTime", m_fDefaultMoodTime);

			// patterns
			int nNumPatterns = 0;
			ser.Value("PatternCount", nNumPatterns);

			for (int i = 0; i < nNumPatterns; ++i)
			{
				ser.BeginGroup("Pattern");
				ser.Value("PatternName",sTempName);

				CMusicPattern* const pPattern = stl::find_in_map(m_mapPatterns, CONST_TEMP_STRING(sTempName), (CMusicPattern *)NULL);

				if (pPattern)
				{
					CMusicPatternInstance* const pPatternInstance = pPattern->CreateInstance();

					if (pPatternInstance)
					{
						m_PatternInstances.push_back(pPatternInstance); // store it to prevent other threads to call destructor

						SMusicPatternPlayInfo NewPlayInfo;

						int nBlendType = 0;
						ser.Value("BlendType", nBlendType);
						NewPlayInfo.eBlendType = (EBlendingType)nBlendType;

						ser.Value("Layer", NewPlayInfo.nLayer);

						int nPlayingType = 0;
						ser.Value("PlayingType", nPlayingType);
						NewPlayInfo.ePlayingType = (EPlayingType)nPlayingType;

						float fPhase = 0.0f;
						ser.Value("Phase", fPhase);
						NewPlayInfo.fPhase = fPhase;

						float fFadeTime = 0.0f;
						ser.Value("FadeTime", fFadeTime);
						NewPlayInfo.fFadeTime = fFadeTime;

						ser.Value("SamplesToWait", NewPlayInfo.nSamplesToWait);
						ser.Value("FadeOutMainTracks", NewPlayInfo.bFadeOutMainTracks);

						NewPlayInfo.pPatternInstance = pPatternInstance;

						ser.Value("PlayFromStart", pPatternInstance->m_bPlayFromStart);
						ser.Value("PlaySingle", pPatternInstance->m_bPlaySingle);
						ser.Value("PlayAtonce", pPatternInstance->m_bPlayAtOnce);

						ser.Value("Pos", NewPlayInfo.nStartFromPos);

						ser.Value("IsTrack1", NewPlayInfo.bForceTrack1);
						ser.Value("IsTrack2", NewPlayInfo.bForceTrack2);

						m_vecQueuedLoadPatterns.push_back(NewPlayInfo);

						ser.Value("PatternTheme", sTempName);

						if (!sTempName.empty())
						{
							ThemeIt = m_mapThemes.find(CONST_TEMP_STRING(sTempName));

							if (ThemeIt != m_mapThemes.end())
							{
								SMusicTheme* pTheme = ThemeIt->second;
								ser.Value("PatternMood", sTempName);

								if (!sTempName.empty())
								{
									TMoodMapIt MoodIt = pTheme->mapMoods.find(CONST_TEMP_STRING(sTempName));

									if (MoodIt != pTheme->mapMoods.end())
									{
										pPatternInstance->SetMood(MoodIt->second);
									}
								}
							}
						}
					}
				}

				ser.EndGroup();
			}

			m_bWaitForQueuedPatterns = true;


			if (m_pCurrMood && !m_pCurrMood->pCurrPatternSet)
			{
				ChoosePatternSet(m_pCurrMood, true);
			}

			// Make sure to update the blend type on still playing patterns.
			bool bCanIncrement = true;
			CryFixedStringT<256> sTempPlayingPatternName;
			CryFixedStringT<256> sTempQueuedPatternName;

			TPatternPlayInfoVec::const_iterator IterPatternEnd(m_vecPlayingPatterns.end());

			for (TPatternPlayInfoVec::iterator IterPattern(m_vecPlayingPatterns.begin()); IterPattern != IterPatternEnd;)
			{
				bCanIncrement = true;
				SMusicPatternPlayInfo& PlayInfo = (*IterPattern);
				sTempPlayingPatternName = PlayInfo.pPatternInstance->GetPattern()->GetName();

				// Make sure this pattern instance is not already waiting in m_vecQueuedLoadPatterns.
				// If so use the one already playing, update it and discard of the queued one.
				TPatternPlayInfoVec::const_iterator IterPatternQueuedEnd(m_vecQueuedLoadPatterns.end());

				for (TPatternPlayInfoVec::iterator IterPatternQueued(m_vecQueuedLoadPatterns.begin()); IterPatternQueued != IterPatternQueuedEnd;)
				{
					SMusicPatternPlayInfo& PlayInfoQueued = (*IterPatternQueued);

					sTempQueuedPatternName = PlayInfoQueued.pPatternInstance->GetPattern()->GetName();

					if (sTempQueuedPatternName.compareNoCase(sTempPlayingPatternName) == 0)
					{
						PlayInfo.ePlayingType  = PlayInfoQueued.ePlayingType;
						PlayInfo.bForceTrack1  = PlayInfoQueued.bForceTrack1;
						PlayInfo.bForceTrack2  = PlayInfoQueued.bForceTrack2;
						//Now since a separate instance will be created for this fading in pattern so we need its start position.
						PlayInfo.nStartFromPos = PlayInfoQueued.nStartFromPos; 

						// This check is necessary to prevent multiple patterns to play during serialization. 
						if (PlayInfoQueued.eBlendType != EBlend_FadeOut)
						{
							// Pattern that are still fading out should fade in again during serialization.
							// Only pattern that fully faded out before start with full volume.
							PlayInfo.eBlendType = EBlend_FadeIn; 
							PlayInfo.nLayer     = PlayInfoQueued.nLayer;
						}

						IterPatternQueued    = m_vecQueuedLoadPatterns.erase(IterPatternQueued);
						IterPatternQueuedEnd = m_vecQueuedLoadPatterns.end();

						// Need to do this now to prevent this pattern from getting faded out again in case an EndTheme request is queued up.
						m_vecQueuedPatterns.push_back(PlayInfo);
						IterPattern    = m_vecPlayingPatterns.erase(IterPattern);
						IterPatternEnd = m_vecPlayingPatterns.end();
						bCanIncrement  = false;
					}
					else
					{
						++IterPatternQueued;
					}
				}

				if (bCanIncrement)
				{
					++IterPattern;
				}
			}

			// Let the streaming thread take care of proper pattern assignment.
			IterPatternEnd = m_vecQueuedLoadPatterns.end();

			for (TPatternPlayInfoVec::iterator IterPattern(m_vecQueuedLoadPatterns.begin()); IterPattern != IterPatternEnd; ++IterPattern)
			{
				PushPatternToMixList(*IterPattern);
			}

			m_vecQueuedLoadPatterns.clear();

			StartPlaying();
		}//end of reading
	}
}



#if !defined(CRYCG_CM)
SPU_ENTRY(MixStreamsKernel)
#endif
void CMusicSystem::MixStreamsKernel(void *const __restrict pBuffer, int nSamplesToCopy, unsigned int mixStreamsPlayInfosCnt, void *pPatternMixBuffer)
{



	memset(m_pMixBuffer, 0, nSamplesToCopy);









	//offset the same as the pBuffer within 16 bytes to have faster memcpy
	void *pMixBuffer = (void*)(&((char*)m_pMixBuffer)[(unsigned int)pBuffer & 15]);			// thats where all is stored to
	for(unsigned int i=0;i<mixStreamsPlayInfosCnt;++i)
	{
		SMusicPatternPlayInfoHolder& PlayInfo = m_MixStreamsPlayInfos[i];
		// get pattern volume
		float fFade = PlayInfo.fFade;

		// quick logarithmic conversion
		float fLog = cry_logf(fFade*1.71f + 1.0f);
		int iLog = static_cast<int>(fLog * float(1<<16));

		signed short *__restrict mixBuffer = (signed short *)pMixBuffer;			// thats where all is stored to
		//signed short *srcMix =	(signed short *)pMixBuffer;			// thats where accumulated data is
		signed short *__restrict srcTrack = (signed short *)((char*)m_pPatternMixBuffer + i*MixBufferSize); // thats where the new data is

		
		// TODO 
		// replace changing mixBuffer adress with increasing count
		// so pattern which start in the middle of a mix-buffer-length allign to
		// the sample correct position they should start
		//if (PlayInfo.nSamplesToWait)
		//{
		//	if (2*PlayInfo.nSamplesToWait < MixBufferSize)
		//		mixBuffer += 2*PlayInfo.nSamplesToWait;
		//	else
		//	{
		//		//assert(!"prevents really bad memory access");
		//	}
		//}

		int iMix[8];
		int iTrack[8];
		int iDest[8];

		// 4 times unrolled
		for (unsigned int count = 0; count < ((PlayInfo.nSamples*2)/8); ++count)
		{
			// platform specific endian conversion is taking care of within the decoders

			// getting the old Data
			int index = count*8;
			
			iMix[0] = mixBuffer[index+0];
			iMix[1] = mixBuffer[index+1];
			iMix[2] = mixBuffer[index+2];
			iMix[3] = mixBuffer[index+3];
			iMix[4] = mixBuffer[index+4];
			iMix[5] = mixBuffer[index+5];
			iMix[6] = mixBuffer[index+6];
			iMix[7] = mixBuffer[index+7];



			// getting the new Data
			iTrack[0] = srcTrack[index+0];
			iTrack[1] = srcTrack[index+1];
			iTrack[2] = srcTrack[index+2];
			iTrack[3] = srcTrack[index+3];
			iTrack[4] = srcTrack[index+4];
			iTrack[5] = srcTrack[index+5];
			iTrack[6] = srcTrack[index+6];
			iTrack[7] = srcTrack[index+7];



			// mixing old and new combined with pattern volume and write to Dest
			iDest[0] = (iMix[0] + ((iTrack[0]*iLog) >> 16));
			iDest[1] = (iMix[1] + ((iTrack[1]*iLog) >> 16));
			iDest[2] = (iMix[2] + ((iTrack[2]*iLog) >> 16));
			iDest[3] = (iMix[3] + ((iTrack[3]*iLog) >> 16));
			iDest[4] = (iMix[4] + ((iTrack[4]*iLog) >> 16));
			iDest[5] = (iMix[5] + ((iTrack[5]*iLog) >> 16));
			iDest[6] = (iMix[6] + ((iTrack[6]*iLog) >> 16));
			iDest[7] = (iMix[7] + ((iTrack[7]*iLog) >> 16));

			// Verify Dest is in the signed short range
			mixBuffer[index+0] = (iDest[0] < SHRT_MIN) ? SHRT_MIN : (iDest[0] > SHRT_MAX) ? SHRT_MAX : iDest[0];
			mixBuffer[index+1] = (iDest[1] < SHRT_MIN) ? SHRT_MIN : (iDest[1] > SHRT_MAX) ? SHRT_MAX : iDest[1];
			mixBuffer[index+2] = (iDest[2] < SHRT_MIN) ? SHRT_MIN : (iDest[2] > SHRT_MAX) ? SHRT_MAX : iDest[2];
			mixBuffer[index+3] = (iDest[3] < SHRT_MIN) ? SHRT_MIN : (iDest[3] > SHRT_MAX) ? SHRT_MAX : iDest[3];
			mixBuffer[index+4] = (iDest[4] < SHRT_MIN) ? SHRT_MIN : (iDest[4] > SHRT_MAX) ? SHRT_MAX : iDest[4];
			mixBuffer[index+5] = (iDest[5] < SHRT_MIN) ? SHRT_MIN : (iDest[5] > SHRT_MAX) ? SHRT_MAX : iDest[5];
			mixBuffer[index+6] = (iDest[6] < SHRT_MIN) ? SHRT_MIN : (iDest[6] > SHRT_MAX) ? SHRT_MAX : iDest[6];
			mixBuffer[index+7] = (iDest[7] < SHRT_MIN) ? SHRT_MIN : (iDest[7] > SHRT_MAX) ? SHRT_MAX : iDest[7];

		}

		// lets process the samples which are left
		for (unsigned int count = 0; count < ((PlayInfo.nSamples*2)%8); ++count)
		{
			int index = ((PlayInfo.nSamples*2)/8)*8 + count;

			// getting the old Data
			iMix[0] = mixBuffer[index+0];

			// getting the new Data
			iTrack[0] = srcTrack[index+0];

			// mixing old and new combined with pattern volume and write to Dest
			iDest[0] = (iMix[0] + ((iTrack[0]*iLog)>>16));
			
			// Verify Dest is in the signed short range
			mixBuffer[index+0] = (iDest[0] < SHRT_MIN) ? SHRT_MIN : (iDest[0] > SHRT_MAX) ? SHRT_MAX : iDest[0];
		}
	}
	// copy the final mixed data from m_pBuffer to the FMOD's pMixBuffer (on PS3 prevents RSX slowdown)
	memcpy(pBuffer, pMixBuffer, nSamplesToCopy);

	#undef m_pMixBuffer
	#undef m_pPatternMixBuffer
	#undef m_MixStreamsPlayInfos
}

//////////////////////////////////////////////////////////////////////////
void CMusicSystem::FadeOutMainTracks(bool const bForceChange)
{
	// If there is something playing on the main tracks fade it out
	if (m_pTrack1 || m_pTrack2)
	{
		TPatternPlayInfoVecIt ItEnd = m_vecPlayingPatterns.end();
		for (TPatternPlayInfoVecIt It=m_vecPlayingPatterns.begin(); It!=ItEnd; ++It)
		{
			SMusicPatternPlayInfo *pPlayInfo = &(*It);

			if ((m_pTrack1 && pPlayInfo->pPatternInstance == m_pTrack1) || (m_pTrack2 && pPlayInfo->pPatternInstance == m_pTrack2))
			{
				if ((((pPlayInfo->nLayer & MUSICLAYER_MAIN_ANY) != 0) || ((pPlayInfo->nLayer & MUSICLAYER_PATTERN_BY_NAME) != 0)) && ((pPlayInfo->nLayer & MUSICLAYER_STINGER) == 0) && (pPlayInfo->ePlayingType != EPlaying_Stop))
				{
					if (bForceChange || pPlayInfo->eBlendType == EBlend_FadeOut || pPlayInfo->eBlendType == EBlend_FadeIn)
						pPlayInfo->eBlendType = EBlend_FadeOut;
					else
						pPlayInfo->eBlendType = EBlend_FadeOutOnFadePoint;

					pPlayInfo->ePlayingType = EPlaying_Once;
				}
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CMusicSystem::ReleaseXMLNode(XmlNodeRef& rXMLNode)
{
	if (rXMLNode)
	{
		assert(rXMLNode->GetRefCount() == 1);
		rXMLNode->removeAllChilds();
		rXMLNode->removeAllAttributes();
		rXMLNode = NULL;
	}
}

void CMusicSystem::OnSystemEvent(ESystemEvent eEvent, UINT_PTR wparam, UINT_PTR lparam)
{
	switch (eEvent)
	{
	case ESYSTEM_EVENT_LEVEL_LOAD_END:
		{
			EndTheme(EThemeFade_FadeOut, 0, true);

			break;
		}
	case ESYSTEM_EVENT_LEVEL_GAMEPLAY_START:
		{
			Pause(false);

			break;
		}
	default:
		{
			break;
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CMusicSystem::AddEventListener(IMusicSystemEventListener* const pListener)
{
	stl::push_back_unique(m_apEventListeners, pListener);
}

//////////////////////////////////////////////////////////////////////////
void CMusicSystem::RemoveEventListener(IMusicSystemEventListener* const pListener)
{
	stl::find_and_erase(m_apEventListeners, pListener);
}

//////////////////////////////////////////////////////////////////////////
void CMusicSystem::QueueEvent(EMusicSystemCallbackEvent const eEvent)
{
	// Events must only be sent from the main thread therefore we queue them up here
	stl::push_back_unique(m_aeQueuedMusicSystemEvents, eEvent);
}

//////////////////////////////////////////////////////////////////////////
void CMusicSystem::SendEventsToListeners()
{
	if (!m_aeQueuedMusicSystemEvents.empty() && !m_apEventListeners.empty())
	{
		// Use a local copy as the member can be manipulated by another thread
		MusicSystemEventListeners const apEventListeners(m_apEventListeners);
		MusicSystemEventQueue const aeEventQueue(m_aeQueuedMusicSystemEvents);

		MusicSystemEventListeners::const_iterator const IterEnd(apEventListeners.end());
		MusicSystemEventQueue::const_iterator const IterEndEvents(aeEventQueue.end());

		for (MusicSystemEventListeners::const_iterator Iter(apEventListeners.begin()); Iter != IterEnd; ++Iter)
		{
			for (MusicSystemEventQueue::const_iterator IterEvents(aeEventQueue.begin()); IterEvents != IterEndEvents; ++IterEvents)
			{
				(*Iter)->OnMusicSystemEvent((*IterEvents), m_oMusicSystemInfo);
			}
		}
	}

	// Always clear the events even if there are no listeners.
	m_aeQueuedMusicSystemEvents.clear();
}

//////////////////////////////////////////////////////////////////////////
void CMusicSystem::SetTrack1(CMusicPatternInstance* const pNewTrack1, int const nLayerType)
{
	if (pNewTrack1 != m_pTrack1)
	{
		size_t nOldValue = m_oMusicSystemInfo.nMainPatternCurrentLoopCount;

		// Reset the counter only when there's a track playing on track #1 or only a track #2 which means this is a fresh start anyway
		if (m_pTrack1 || (!m_pTrack1 && !m_pNextTrack))
		{
			m_oMusicSystemInfo.nMainPatternCurrentLoopCount = 0;
		}

		CMusicPattern const* pPattern1 = NULL;
		CMusicPattern const* pPattern2 = NULL;

		if (!pNewTrack1 && m_pTrack2)
		{
			// In case the current track #1 is getting removed have a look at track #2 if something is waiting there and compare
			pPattern1 = m_pTrack1->GetPattern();
			pPattern2 = m_pTrack2->GetPattern();
		}
		else if (pNewTrack1 && m_pTrack1)
		{
			// When there's a real new track compare these 2 directly
			pPattern1 = pNewTrack1->GetPattern();
			pPattern2 = m_pTrack1->GetPattern();
		}

		if (pPattern1 && pPattern2)
		{
			stack_string const sPatternName1(pPattern1->GetName());

			if (!sPatternName1.empty())
			{
				// If the patterns on track #1 and track #2 match it's a loop
				if (sPatternName1.compareNoCase(pPattern2->GetName()) == 0)
				{
					m_oMusicSystemInfo.nMainPatternCurrentLoopCount = ++nOldValue;
				}
			}
		}

		m_pTrack1    = pNewTrack1;
		m_Track1Type = (m_pTrack1 != NULL) ? nLayerType : MUSICLAYER_NONE;

		QueueEvent(MUSICSYSTEM_EVENT_ON_UPDATE_LOOP_COUNT);
	}
}

//////////////////////////////////////////////////////////////////////////
void CMusicSystem::SetTrack2(CMusicPatternInstance* const pNewTrack2, int const nLayerType)
{
	m_pTrack2    = pNewTrack2;
	m_Track2Type = (m_pTrack2 != NULL) ? nLayerType : MUSICLAYER_NONE;
}

//////////////////////////////////////////////////////////////////////////
void CMusicSystem::SetNextTrack(CMusicPatternInstance* const pNewNextTrack, int const nLayerType)
{
	m_pNextTrack    = pNewNextTrack;
	m_NextTrackType = (m_pNextTrack != NULL) ? nLayerType : MUSICLAYER_NONE;
}

//////////////////////////////////////////////////////////////////////////
void CMusicSystem::ForceFinishLoadingPattern(CMusicPatternInstance const* const pPatternToFinish) const
{
	// This method makes sure to first "finish" patterns that are still loading so it's safe to release them.
	CSoundSystem const* const pSoundSystem     = static_cast<CSoundSystem const* const>(gEnv->pSoundSystem);
	CFileCacheManager* const pFileCacheManager = pSoundSystem->GetFileCacheManager();

	while (!pPatternToFinish->IsReleasable())
	{
		// Keep updating the stream engine for music files that are not cached in memory.
		gEnv->pSystem->GetStreamEngine()->Update(STREAM_TASK_TYPE_AUDIO_ALL);

		// Keep updating the AFCM for music files that are cached to it.
		pFileCacheManager->Update();

		// This will update the open state members.
		pPatternToFinish->GetDecoderInstance()->Init();

		// Sleep a bit.
		CrySleep(10);
	}
}

//////////////////////////////////////////////////////////////////////////
void CMusicSystem::ForceFinishAllPatterns()
{
	// Playing patterns
	TPatternPlayInfoVecItConst ItInfoEnd(m_vecPlayingPatterns.end());

	for (TPatternPlayInfoVecItConst It(m_vecPlayingPatterns.begin()); It != ItInfoEnd; ++It)
	{
		SMusicPatternPlayInfo const* const pPlayInfo = &(*It);

		if (pPlayInfo->pPatternInstance && !pPlayInfo->pPatternInstance->IsReleasable())
		{
			ForceFinishLoadingPattern(pPlayInfo->pPatternInstance);
		}
	}

	stl::free_container(m_vecPlayingPatterns);

	// Queued patterns
	ItInfoEnd = m_vecQueuedPatterns.end();

	for (TPatternPlayInfoVecItConst It(m_vecQueuedPatterns.begin()); It != ItInfoEnd; ++It)
	{
		SMusicPatternPlayInfo const* const pPlayInfo = &(*It);

		if (pPlayInfo->pPatternInstance && !pPlayInfo->pPatternInstance->IsReleasable())
		{
			ForceFinishLoadingPattern(pPlayInfo->pPatternInstance);
		}
	}

	stl::free_container(m_vecQueuedPatterns);

	// Queued load patterns
	ItInfoEnd = m_vecQueuedLoadPatterns.end();

	for (TPatternPlayInfoVecItConst It(m_vecQueuedLoadPatterns.begin()); It != ItInfoEnd; ++It)
	{
		SMusicPatternPlayInfo const* const pPlayInfo = &(*It);

		if (pPlayInfo->pPatternInstance && !pPlayInfo->pPatternInstance->IsReleasable())
		{
			ForceFinishLoadingPattern(pPlayInfo->pPatternInstance);
		}
	}

	stl::free_container(m_vecQueuedLoadPatterns);

	// Instances to be removed
	TPatternInstancesVecItConst ItEnd(m_PatternInstancesToBeRemoved.end());

	for (TPatternInstancesVecItConst It(m_PatternInstancesToBeRemoved.begin()); It != ItEnd; ++It)
	{
		CMusicPatternInstance const* const pInstance = (*It);

		if (pInstance && !pInstance->IsReleasable())
		{
			ForceFinishLoadingPattern(pInstance);
		}
	}

	stl::free_container(m_PatternInstancesToBeRemoved);

	// Pattern instances
	ItEnd = m_PatternInstances.end();

	for (TPatternInstancesVecItConst It(m_PatternInstances.begin()); It != ItEnd; ++It)
	{
		CMusicPatternInstance const* const pInstance = (*It);

		if (pInstance && !pInstance->IsReleasable())
		{
			ForceFinishLoadingPattern(pInstance);
		}
	}

	stl::free_container(m_PatternInstances);

	// Pattern instances removed from streaming thread
	ItEnd = m_PatternInstancesRemovedFromStreamingThread.end();

	for (TPatternInstancesVecItConst It(m_PatternInstancesRemovedFromStreamingThread.begin()); It != ItEnd; ++It)
	{
		CMusicPatternInstance const* const pInstance = (*It);

		if (pInstance && !pInstance->IsReleasable())
		{
			ForceFinishLoadingPattern(pInstance);
		}
	}

	stl::free_container(m_PatternInstancesRemovedFromStreamingThread);
}

#include UNIQUE_VIRTUAL_WRAPPER(IStringItVec)
